spring data projections
Créé le : 21 avril 2025
Créé le : 21 avril 2025
spring data projections
What a “projection” is
In Spring Data a projection is simply a different return‐type for a repository method that exposes only a subset (or a derived view) of the entity’s attributes, so you can:
javapublic interface NamesOnly { // “closed” projection String getFirstname(); String getLastname(); } interface PersonRepository extends Repository<Person, UUID> { Collection<NamesOnly> findByLastname(String lastname); }
Spring creates a lightweight proxy that implements NamesOnly
, executes a query that selects only the two columns, and forwards getter calls to the underlying entity. Home
javainterface PersonSummary { String getFirstname(); AddressView getAddress(); interface AddressView { String getCity(); } }
Works “downward” from the owning side; the nested object is wrapped in its own proxy. Home
javapublic interface FullName { @Value("#{target.firstname + ' ' + target.lastname}") String getFullName(); }
Because Spring can’t predict which columns your SpEL will touch, it disables the SQL column pruning optimisation—use sparingly. Baeldung
For immutable DTOs or Java records you can use a constructor (parameter names must match entity field names or column aliases):
javapublic record PersonCard(String firstname, String lastname) {} @Query(""" select new com.example.PersonCard(p.firstname, p.lastname) from Person p where p.lastname = :ln """) List<PersonCard> findCardsByLastname(String ln);
No proxies, no nested projections, but you get an actual instance of your class/record. Home
java<T> List<T> findByLastname(String lastname, Class<T> type);
Callers decide on the fly:
javarepo.findByLastname("Matthews", Person.class); // full entity repo.findByLastname("Matthews", NamesOnly.class); // interface repo.findByLastname("Matthews", PersonCard.class); // DTO ``` [Home](https://docs.spring.io/spring-data/jpa/reference/data-commons/repositories/projections.html) --- ## 4 Other useful patterns & tips | Pattern / feature | When to use | Gotchas | |-------------------|-------------|---------| | **Value Expressions** (new in Spring Data 2024.x) | Simple SpEL transformations in interface projections without `@Value` string‑literals | Supported in milestone releases 2024.0+; still experimental. [Home](https://spring.io/blog/2024/02/16/first-milestone-of-spring-data-2024-0-0-available) | | **Nullable wrappers** (`Optional`, `scala.Option`, etc.) | Make nullable columns explicit in your API | Works only for interface projections. [Home](https://docs.spring.io/spring-data/jpa/reference/data-commons/repositories/projections.html) | | **Pagination + projections** | Works out of the box; use `Page<NamesOnly>` | Don’t override base `findAll(Pageable)`—declare a separate method or Spring will ignore your projection. [Home](https://docs.spring.io/spring-data/jpa/reference/data-commons/repositories/projections.html) | | **Native / JPQL queries** | When you need joins/aggregates not derivable from method name | Ensure column aliases match constructor params or interface getter names. | | **EntityGraph vs. projection** | Use an `@EntityGraph` when you *still need* full entities but want to pre‑fetch relations; use a projection when you truly want fewer columns. | --- ## 5 Performance notes * Closed interface or DTO projections let Spring Data (JPA & relational, MongoDB, Elasticsearch, …) generate a `SELECT` that contains **only the mapped columns**, which can cut network traffic and object hydration cost dramatically. * Open projections forfeit that optimisation—test the actual query in your logs (`spring.jpa.show-sql=true`) before you celebrate. * Mixing projections with **`fetch = FetchType.LAZY`** collections is safe: because you never touch the entity, Hibernate won’t trigger extra selects (the proxy simply lacks those getters). --- ## 6 Common pitfalls 1. **Overriding repository base methods** – the base implementation wins, so you’ll get entities instead of your projection. Declare a *new* method name (`findAllBy`, `queryAll…`) instead. [Home](https://docs.spring.io/spring-data/jpa/reference/data-commons/repositories/projections.html) 2. **Alias mismatch in constructor expressions** – column order is irrelevant; names matter. 3. **Mutation** – projections are *read‑only*. To update, load the entity or use `@Modifying` queries. 4. **Pagination + native queries** – remember to supply a separate `countQuery` because the projection `SELECT` might not be valid for counts. --- ### TL;DR – choosing the right style | Need | Best choice | |------|-------------| | Minimal columns, easy | **Closed interface** | | Derived/calculated field | **Open interface** (or default‑method) | | Public API DTO / Record | **DTO projection** | | Pick type at runtime | **Dynamic projection** | | Complex aggregation | Custom JPQL / native query + DTO | With these patterns in your toolbox you can shape Spring Data’s output instead of post‑processing it—your SQL, network, and serialization layers will thank you. 🛠️