projections: Spring Data JPA interface, record, nested and dynamic projections with the SQL they generate

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_017ic4b5sejRr8s3kapmRY2g
This commit is contained in:
2026-11-12 23:51:00 +05:30
co-authored by Claude Sonnet 5.5
parent 7d7d4e872e
commit e3d1b550ae
30 changed files with 710 additions and 0 deletions
+44
View File
@@ -0,0 +1,44 @@
# projections
Companion code for **Spring Data JPA Projections: Interface, Record and Dynamic Projections (With the SQL They Generate)** on [ankurm.com](https://ankurm.com).
| Component | Version |
|---|---|
| Spring Boot | 4.1.2 |
| Spring Data JPA | 4.1.2 |
| Hibernate ORM | 7.4.13.Final |
| H2 (in-memory) | 2.4.240 |
| JDK | Temurin 25.0.5 LTS |
No Docker needed. Every SQL statement is captured by `SqlRecorder`, a Hibernate `StatementInspector`, and every test writes the SQL it saw to `output/` while asserting the same facts, so the numbers in the post fail the build if they stop being true.
```bash
./scripts/run-all.sh # regenerates every file in output/
```
| Source file | What it shows |
|---|---|
| `EmployeeSummary` | closed interface projection |
| `EmployeeFullName` | open interface projection (`@Value` SpEL) |
| `EmployeeFullNameClosed` | the fix for the open projection: a default method |
| `EmployeeWithDepartment` | nested interface projection |
| `EmployeeDto` | record DTO projection |
| `DepartmentPay` | target of a JPQL constructor expression |
| `EmployeeRepository` | every query-method form, numbered 1 to 14 |
| `SqlRecorder` | the SQL capture mechanism |
| Transcript | Shows |
|---|---|
| `output/01-entity-vs-closed-vs-dto.txt` | 7 columns vs 2 columns, managed entities 4 vs 0 |
| `output/02-open-projection-trap.txt` | an open projection selects all 7 columns |
| `output/11-default-method-fix.txt` | a default method keeps the projection closed (2 columns) |
| `output/03-nested-projection.txt` | nested projection pulls the whole department row |
| `output/04-dynamic-projection.txt` | one method, three shapes |
| `output/05-query-forms.txt` | `@Query` aliases, constructor expression, native query |
| `output/06-pitfall-entity-query.txt` | `select e` declared as an interface projection loads everything |
| `output/07-pitfall-missing-aliases.txt` | tuple select without aliases returns nulls |
| `output/08-pitfall-unknown-property.txt` | unknown projection property |
| `output/09-paging-and-proxy.txt` | paging runs a count query; projections are JDK proxies |
| `output/10-entity-query-as-dto-rewrite.txt` | `select e` declared as a record is rewritten to 2 columns |
There is no `docs/` folder: the explanations live in the post.