
Specifications와Query By Example은 실무에서 거의 사용하지 않고Querydsl로 대체되기 때문에 생략!
Projections ]엔티티 대신
DTO를 편리하게 조회할 때 사용한다.
전체 엔티티 대신 회원 이름만 조회하고 싶은 경우를 가정해보자!
Closed Projectionspublic interface UsernameOnly {
String getUsername();
}
getter 형식으로 지정하면 해당 필드만 선택해서 조회한다. (Projection)public interface MemberRepositry ... {
List<UsernameOnly> findProjectionsByUsername(@Param("username") String username);
}

username만 조회된 것을 확인할 수 있다!Open Projectionspublic interface UsernameOnly {
@Value("#{target.username + ' ' + target.age}")
String getUsername();
}
SpEL 문법도 제공한다.SpEL 문법을 사용하면 DB에서 엔티티 필드를 다 조회해온 다음에 계산한다
Projectionpublic class UsernameOnlyDto {
private final String username;
public UsernameOnlyDto(String username) {
this.username = username;
}
public String getUsername() {
return username;
}
}
DTO 형식도 가능하다.
Projections<T> List<T> findProjectionsByUsername(@Param("username") String username, Class<T> type);
Generic type을 주면 동적으로 프로젝션 데이터를 변경할 수 있다.public interface NestedClosedProjection {
String getUsername();
TeamInfo getTeam();
interface TeamInfo {
String getName();
}
}

member는 username만, team은 모두 조회되었다.🚫 프로젝션 사용 시 주의할 점
- 프로젝션 대상이
root엔티티면, JPQL SELECT절 최적화가 가능하다!- 프로젝션 대상이
root가 아니면,
LEFT OUTER JOIN처리- 모든 필드를 SELECT해서 엔티티로 조회한 뒤에 계산한다.
📌 정리!
- 프로젝션 대상이
root인 경우projection이 유용하지만!
root엔티티를 넘어가면 JPQL SELECT 최적화가 안된다.
→ 즉, 실무의 복잡한 쿼리를 해결하기에는 한계가 있다!- 실무에서는 단순할 때만 사용하고, 조금만 복잡해지면
QueryDSL을 사용하자!
가급적 네이티브 쿼리를 사용하지 않는 것이 좋다.
최신 버전에서 스프링 데이터Projections가 지원되므로 이 기능을 사용하자!
Object[]TupleDTO (스프링 데이터 인터페이스 Projections 지원)Sort 파라미터를 통한 정렬이 정상 동작하지 않을 수 있다. (믿지 말고 직접 처리)public interface MemberRepository extends JpaRepository<Member, Long> {
@Query(value = "select * from member where username = ?", nativeQuery = true)
Member findByNativeQuery(String username);
}
@SqlResultSetMapping → 복잡Hibernate ResultTransformer를 사용해야 한다. → 복잡JdbcTemplate or myBatis 권장Projections 활용예) 스프링 데이터 JPA 네이티브 쿼리 + 인터페이스 기반 Projections 활용
@Query(value = "select m.member_id as id, m.username, t.name as teamName " +
"from member m left join team t",
countQuery = "select count(*) from member",
nativeQuery = true)
Page<MemberProjection> findByNativeProjection(Pageable pageable);