Author
In real-world applications, fetching entire entities is often unnecessary and inefficient. Most use cases—such as REST APIs, dashboards, reports, and list views—require only a subset of fields.
Fetching full entities along with their relationships can lead to:
DTO Projections in Spring Data JPA solve this by allowing you to fetch only the required columns directly from the database and map them into DTOs (Data Transfer Objects).
A DTO Projection retrieves partial data from the database and maps it directly into:
Instead of returning managed JPA entities, repository methods return DTOs.
LazyInitializationException? DTO projections are faster, safer, and cleaner than entities when updates are not required.
Spring Data JPA supports three main projection types:

Spring Data JPA automatically creates a proxy implementation and fetches only the fields declared in the interface.
Projection Interface
publicinterfaceUserSummary {
LonggetId();
StringgetName();
StringgetEmail();
}
Repository
publicinterfaceUserRepositoryextendsJpaRepository<User, Long> {
List<UserSummary>findByActiveTrue();
}
Uses JPQL’s new keyword to map query results into a DTO.
DTO Class
publicclassUserDTO {
private Long id;
private String name;
private String email;
publicUserDTO(Long id, String name, String email) {
this.id = id;
this.name = name;
this.email = email;
}
}
Repository
@Query("""
SELECT new com.example.dto.UserDTO(u.id, u.name, u.email)
FROM User u
WHERE u.active = true
""")
List<UserDTO>findActiveUsers();
Useful when the same query must return different views of data.
Repository
publicinterfaceUserRepositoryextendsJpaRepository<User, Long> {
<T> List<T>findByActiveTrue(Class<T> type);
}
Usage
List<UserSummary> summaries =
userRepository.findByActiveTrue(UserSummary.class);
List<UserDTO> dtos =
userRepository.findByActiveTrue(UserDTO.class);
DTO projections can include nested objects using interfaces.
publicinterfaceOrderView {
LonggetId();
UserInfogetUser();
interfaceUserInfo {
StringgetName();
StringgetEmail();
}
}
List<OrderView>findAll();
⚠️ Overusing nested projections may still trigger extra joins or queries.
| Feature | DTO Projection | Entity Fetching |
|---|---|---|
| Data fetched | Only selected fields required for the use case are fetched from the database | The complete entity with all mapped fields is fetched |
| Performance | Faster because less data is retrieved and processed | Slower because the full entity is loaded |
| Memory usage | Lower memory consumption since only required fields are stored | Higher memory usage because full entities are kept in memory |
| Change tracking | Changes are not tracked because DTOs are not managed by JPA | Changes are tracked automatically by the JPA persistence context |
| Lazy loading | Lazy loading is not applicable because DTOs are not entities | Lazy loading is supported for entity relationships |
| N+1 query risk | Very low risk because data is fetched explicitly in queries | Possible if lazy associations are accessed without proper fetch strategy |
| Updates allowed | Read-only and should not be used for update operations | Supports create, update, and delete operations |
| Transaction requirement | Usually not required for simple read-only queries | Required, especially when using lazy-loaded associations |
| API safety | Safe for APIs because internal entity structure is hidden | Risky for APIs because entities may expose unwanted data |
| Best for APIs | Highly recommended for REST APIs and external clients | Not recommended for direct API exposure |
| Best use case | Read-only views, reports, dashboards, and API responses | Business logic, data manipulation, and persistence operations |
Key Takeaway (Easy Rule)
DTO Projection → When you only read data
Entity Fetching → When you modify data
@EntityGraph| Feature | DTO Projection | @EntityGraph |
|---|---|---|
| What it returns | Returns a Data Transfer Object containing only selected fields | Returns a fully managed JPA entity |
| Primary use case | Best for read-only data such as API responses, dashboards, and reports | Best for business logic where entity behavior and updates are required |
| Fetch control | Controls exactly which columns are selected from the database | Controls which related entities are fetched eagerly |
| Updates allowed | Not allowed because DTOs are not managed by JPA | Allowed because entities are managed by the persistence context |
| Performance | Very high because only required fields are fetched | Good, but slower than DTOs because full entities are loaded |
| Memory usage | Low because partial data is stored | Higher because full entities and relationships are kept in memory |
| Lazy loading behavior | Not applicable because DTOs do not support lazy loading | Fully supported for non-selected associations |
| Risk of N+1 problem | Very low because data is fetched explicitly | Possible if associations are not included in the entity graph |
| Best for REST APIs | Highly recommended and safe | Not recommended for direct exposure |
| Complexity | Simple and lightweight | More complex due to entity management |
Controller
|
|--> Repository
|
|--> DTO Projection
|
|--> Only required columns
@EntityGraph (not a replacement)