Advertisement

Google Ad Slot: content-top

Spring Boot JPA Projection (Fetching Specific Columns)


Projection in Spring Boot JPA allows you to fetch specific columns from a table instead of the entire entity. This improves performance by reducing data transfer.


Using Interface-Based Projection

The simplest way to fetch specific columns is by defining an interface projection


Example: Fetching Only id and name from User

User.java
@Entity
@Table(name = "user")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

private String name;
private String email;
private int age;

// Getters and Setters
}

Define a Projection Interface
public interface UserProjection {
Long getId();
String getName();
}

Modify Repository to Use Projection
public interface UserRepository extends JpaRepository<User, Long> {
// Fetch only id and name
List<UserProjection> findByAgeGreaterThan(int age);
}
Fetch Data in Service Layer
@Service
public class UserService {
@Autowired
private UserRepository userRepository;

public List<UserProjection> getUsersByAge(int age) {
return userRepository.findByAgeGreaterThan(age);
}
}
Response JSON (Example)
[
{ "id": 1, "name": "John" },
{ "id": 2, "name": "Sathish" }
]

Advantage:Faster than fetching the entire entity.


Using JPQL with Projection

If you need custom queries, you can use JPQL.

Modify Repository with @Query
public interface UserRepository extends JpaRepository<User, Long> {
@Query("SELECT u.id, u.name FROM User u WHERE u.age > :age")
List<Object[]> findUserNamesByAge(@Param("age") int age);
}
Convert Object Array in Service Layer
public List<Map<String, Object>> getUserNamesByAge(int age) {
List<Object[]> users = userRepository.findUserNamesByAge(age);
return users.stream()
.map(obj -> Map.of("id", obj[0], "name", obj[1]))
.collect(Collectors.toList());
}
Expected Response
[
{ "id": 1, "name": "John" },
{ "id": 2, "name": "Sathish" }
]

Advantage: More flexible, can handle complex queries.


Using DTO (Data Transfer Object) Projection

 Example: Fetching id, name, and age into a DTO


UserDTO.java
public class UserDTO {
private Long id;
private String name;
private int age;

public UserDTO(Long id, String name, int age) {
this.id = id;
this.name = name;
this.age = age;
}

// Getters
public Long getId() { return id; }
public String getName() { return name; }
public int getAge() { return age; }
}
Modify Repository to Use DTO
public interface UserRepository extends JpaRepository<User, Long> {
@Query("SELECT new com.example.dto.UserDTO(u.id, u.name, u.age) FROM User u WHERE u.age > :age")
List<UserDTO> findUsersByAge(@Param("age") int age);
}
Service Method
public List<UserDTO> getUsersByAge(int age) {
return userRepository.findUsersByAge(age);
}
Expected Response
[
{ "id": 1, "name": "John", "age": 30 },
{ "id": 2, "name": "Sathish", "age": 28 }
]

Advantage: Works well with custom DTO transformations.


Native SQL Queries with Projection

If you need raw SQL, use @Query(nativeQuery = true).

Modify Repository for Native Query
public interface UserRepository extends JpaRepository<User, Long> {
@Query(value = "SELECT id, name FROM users WHERE age > :age", nativeQuery = true)
List<Object[]> findUsersByAgeNative(@Param("age") int age);
}
Convert Object Array in Service Layer
public List<Map<String, Object>> getUsersByAgeNative(int age) {
List<Object[]> users = userRepository.findUsersByAgeNative(age);
return users.stream()
.map(obj -> Map.of("id", obj[0], "name", obj[1]))
.collect(Collectors.toList());
}
Expected Response
[
{ "id": 1, "name": "John" },
{ "id": 2, "name": "Sathish" }
]

Advantage: Ideal for complex SQL queries with joins, group by, etc.