spring data projections

تم الإنشاء في: ٢١ أبريل ٢٠٢٥

تمت الإجابة باستخدام O3 بواسطة Chat01

سؤال

spring data projections

الفكرة

Explaining Spring Data Projections

I’m preparing to explain when to use projections and their benefits, along with some practical examples. For instance:

interface NamesOnly { String getFirstname(); String getLastname(); }

Then, the repository:

Collection<NamesOnly> findByLastname(String lastname);

I’ll also include DTOs and dynamic examples, plus highlight the differences between open and closed projections, performance considerations, and how @EntityGraph, projections, and fetch joins compare. I'll mention the limitations, such as issues with mapping nested collections, and the unification of value expressions inside SpEL, with a disclaimer.

Thought for 45 seconds

الإجابة

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:

  •  cut the amount of data fetched from the database;
  •  avoid mapping every field to the JVM when you only need a few columns;
  •  shape the JSON you expose (e.g. Spring Data REST / Spring WebFlux) without writing extra DTO mapping code. Home

1  Interface‑based projections

java
public 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

1.1  Nested projections

java
interface 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

1.2  Open projections (computed fields)

java
public 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


2  DTO / class‑based projections

For immutable DTOs or Java records you can use a constructor (parameter names must match entity field names or column aliases):

java
public 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


3  Dynamic projections (generic)

java
<T> List<T> findByLastname(String lastname, Class<T> type);

Callers decide on the fly:

java
repo.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. 🛠️

مشاركة هذا السؤال والجواب