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
@@ -0,0 +1,11 @@
# entity result: findByDepartmentName("Sales")
select e1_0.id,e1_0.bio,e1_0.department_id,e1_0.email,e1_0.first_name,e1_0.last_name,e1_0.salary from employee e1_0 left join department d1_0 on d1_0.id=e1_0.department_id where d1_0.name=?
columns selected: 7, entities now managed by the persistence context: 4
# closed interface projection: findSummaryByDepartmentName("Sales")
select e1_0.first_name,e1_0.last_name from employee e1_0 left join department d1_0 on d1_0.id=e1_0.department_id where d1_0.name=?
columns selected: 2, entities now managed by the persistence context: 0
# record DTO projection: findDtoByDepartmentName("Sales")
select e1_0.first_name,e1_0.last_name from employee e1_0 left join department d1_0 on d1_0.id=e1_0.department_id where d1_0.name=?
columns selected: 2, entities now managed by the persistence context: 0
@@ -0,0 +1,4 @@
# open projection: findFullNameByDepartmentName("Sales") (getFullName uses @Value SpEL)
select e1_0.id,e1_0.bio,e1_0.department_id,e1_0.email,e1_0.first_name,e1_0.last_name,e1_0.salary from employee e1_0 left join department d1_0 on d1_0.id=e1_0.department_id where d1_0.name=?
columns selected: 7, entities now managed by the persistence context: 4
getFullName() of first row: First1 Last1
@@ -0,0 +1,5 @@
# nested projection: asks for firstName and department.name only
select e1_0.first_name,d1_0.id,d1_0.location,d1_0.name from employee e1_0 left join department d1_0 on d1_0.id=e1_0.department_id where d1_0.name=?
columns selected: 4 (1 from employee + 3 from department), entities now managed: 1
department name read from the projection: Sales
extra statements after reading it: 0
@@ -0,0 +1,4 @@
# one repository method, three result shapes: findByDepartmentName("Sales", Class<T>)
Employee -> 4 rows, 7 columns: select e1_0.id,e1_0.bio,e1_0.department_id,e1_0.email,e1_0.first_name,e1_0.last_name,e1_0.salary
EmployeeSummary -> 4 rows, 2 columns: select e1_0.first_name,e1_0.last_name
EmployeeDto -> 4 rows, 2 columns: select e1_0.first_name,e1_0.last_name
+13
View File
@@ -0,0 +1,13 @@
# @Query with aliases, declared as an interface projection
select e1_0.first_name,e1_0.last_name from employee e1_0 where e1_0.salary>?
rows: 6, first getFirstName(): First6
# @Query with a constructor expression (select new ...DepartmentPay)
select d1_0.name,count(e1_0.id),sum(e1_0.salary) from employee e1_0 join department d1_0 on d1_0.id=e1_0.department_id group by d1_0.name order by d1_0.name
DepartmentPay[department=Engineering, headcount=4, totalSalary=218000.00]
DepartmentPay[department=HR, headcount=4, totalSalary=226000.00]
DepartmentPay[department=Sales, headcount=4, totalSalary=222000.00]
# native query with column aliases equal to the getter names
select first_name as firstName, last_name as lastName from employee where salary > ?
rows: 6, first getFirstName(): First6
@@ -0,0 +1,3 @@
# @Query("select e from Employee e ...") declared as List<EmployeeSummary>
select e1_0.id,e1_0.bio,e1_0.department_id,e1_0.email,e1_0.first_name,e1_0.last_name,e1_0.salary from employee e1_0 where e1_0.salary>?
columns selected: 7, entities now managed: 6, rows: 6
@@ -0,0 +1,5 @@
# @Query("select e.firstName, e.lastName from Employee e ...") with NO aliases
select e1_0.first_name,e1_0.last_name from employee e1_0 where e1_0.salary>?
rows: 6
getFirstName() of first row: null
getLastName() of first row: null
@@ -0,0 +1,3 @@
# dynamic projection onto an interface with getNickname(), which Employee does not have
org.springframework.data.core.PropertyReferenceException
No property 'nickname' found for type 'Employee'
@@ -0,0 +1,8 @@
# Page<EmployeeSummary>, page size 2
select e1_0.first_name,e1_0.last_name from employee e1_0 left join department d1_0 on d1_0.id=e1_0.department_id where d1_0.name=? fetch first ? rows only
select count(*) from employee e1_0 left join department d1_0 on d1_0.id=e1_0.department_id where d1_0.name=?
statements: 2, rows in page: 2, total elements: 4
# what an interface projection really is at runtime
is a java.lang.reflect.Proxy: true
is an Employee: false
@@ -0,0 +1,3 @@
# @Query("select e from Employee e ...") declared as List<EmployeeDto> (a record)
select e1_0.first_name,e1_0.last_name from employee e1_0 where e1_0.salary>?
columns selected: 2, entities now managed: 0, rows: 6
@@ -0,0 +1,4 @@
# closed projection with a default method: findFullNameClosedByDepartmentName("Sales")
select e1_0.first_name,e1_0.last_name from employee e1_0 left join department d1_0 on d1_0.id=e1_0.department_id where d1_0.name=?
columns selected: 2, entities now managed by the persistence context: 0
getFullName() of first row: First1 Last1