Spring Data JPA를 이용해 개발하던 중 모든 컬럼을 조회할 필요없이 특정 컬럼만 조회하면 되는 경우가 생겼다.
이런 경우 Projection을 사용하는데, 정확한 사용법을 알아보자.
class Person {
@Id UUID id;
String firstname, lastname;
Address address;
static class Address {
String zipCode, city, street;
}
}
interface PersonRepository extends Repository<Person, UUID> {
Collection<Person> findByLastname(String lastname);
}
Interface-based Projections
위와 같은 코드에서 이름만 필요한 경우 Interface-based Projections 을 사용할 수 있다.
사용법은 아래 코드와 같이 해당 속성을 읽을 수 있도록 하는 접근 메서드를 선언한 인터페이스를 이용하는 것이다.
주의할 점은 aggregate root의 속성값과 같게 적어줘야한다!
aggregate root : 해당 repository에서 사용하는 entity를 의미(정확한 개념은 추가공부 필요)
interface NamesOnly {
String getFirstname();
String getLastname();
}
interface PersonRepository extends Repository<Person, UUID> {
Collection<NamesOnly> findByLastname(String lastname);
}
쿼리 실행 엔진은 런타임 시점에 해당 인터페이스에 대한 프록시 객체를 만들고 접근 메서드에 대한 호출을 해당 객체로 전달한다.
재귀적 사용
projections는 재귀적으로 사용이 가능하다.
interface PersonSummary {
String getFirstname();
String getLastname();
AddressSummary getAddress();
interface AddressSummary {
String getCity();
}
}
Closed Projections
aggregate와 속성이 일치하는 인터페이스는 Closed Projection이다.
Closed Projection을 사용하면 프록시에 필요한 모든 속성을 알기 때문에 쿼리 실행 최적화가 가능하다.
//Closed Projection
interface NamesOnly {
String getFirstname();
String getLastname();
}
//Open Projection
interface NamesOnly {
@Value("#{target.firstname + ' ' + target.lastname}")
String getFullName();
…
}
Class-based Projections (DTOs)
프록시가 발생하지 않으며 재귀적인 Projection을 사용할 수 없다는 것 빼고는 인터페스 기반 projection과 같다.
생성자의 매개변수 이름으로 값을 읽을 필드를 결정한다.
class NamesOnly {
private final String firstname, lastname;
NamesOnly(String firstname, String lastname) {
this.firstname = firstname;
this.lastname = lastname;
}
String getFirstname() {
return this.firstname;
}
String getLastname() {
return this.lastname;
}
// equals(…) and hashCode() implementations
}
주의할 점으로 클래스 기반 projection은 JPQL의 native 쿼리와 사용할 수 없다.
출처
Spring Data JPA - Reference Documentation
Example 119. Using @Transactional at query methods @Transactional(readOnly = true) interface UserRepository extends JpaRepository { List findByLastname(String lastname); @Modifying @Transactional @Query("delete from User u where u.active = false") void del
docs.spring.io
'Spring' 카테고리의 다른 글
| CustomOidcUserService 구현 (0) | 2025.09.09 |
|---|---|
| 프레임워크를 사용하는 이유 (0) | 2023.10.06 |
| @Transactional의 작동 방식과 롤백되지 않는 문제(Checked Exception) (0) | 2023.08.31 |