→ Specifications, Query By Example은 실무에서 거의 사용되지 않고 QueryDSL로 대체되기 때문에 생략합니다.
Projections
→ 엔티티 대신에 DTO를 편리하게 조회할 때 사용한다. Case_회원 이름만 조회하고 싶은 경우
1.
인터페이스 기반 Closed Projections
public interface UsernameOnly{
String getUsername();
}
public interface MemberRepository ... {
List<UsernameOnly> findProjectionsByUsername(String username);
}
Java
복사
•
조회할 엔티티의 필드를 getter형식으로 지정하면 해당 필드만 선택해서 조회한다.(Projection)
→ 프로퍼티 형식(getter)의 인터페이스를 제공하면, 구현체는 스프링 데이터 JPA가 제공한다.
2.
인터페이스기반 Open Projections → SpEL문법 지원
public interface UsernameOnly{
@Value("#{target.username + ' ' + target.age + ' ' + target.team.name}")
String getUsername();
}
Java
복사
•
SpEL문법을 사용하면, DB에서 엔티티 필드를 모두 조회한 다음 계산한다.
→JPQL SELECT 절 최적화가 안된다.
3.
클래스 기반 Projections
→ 다음과 같이 인터페이스가 아닌 구체적인 DTO형식도 가능하다.
→ 생성자의 파라미터 이름으로 매칭한다.
public class UsernameOnlyDto {
private final String username;
public UsernameOnlyDto(String username) {
this.username = username;
}
public String getUsername() {
return username;
}
}
Java
복사
4.
동적 Projections
→ 아래와 같이 Generic type을 주면, 동적으로 프로젝션 데이터 변경 가능
<T> List<T> findProjectionsByUsername(String username, Class<T> type);
Java
복사
→ 사용코드
List<UsernameOnly> result = memberRepository.findProjectionsByUsername("m1",UsernameOnly.class);
Java
복사
중첩 구조 처리
public interface NestedClosedProjection {
String getUsername();
TeamInfo getTeam();
interface TeamInfo {
String getName();
}
}
/* 실행 쿼리 */
select
m.username as col_0_0_,
t.teamid as col_1_0_,
t.teamid as teamid1_2_,
t.name as name2_2_
from
member m
left outer join
team t
on m.teamid=t.teamid
where
m.username=?
Java
복사
주의
•
프로젝션 대상이 root 엔티티면, JPQL SELECT 절 최적화 가능
•
프로젝션 대상이 root가 아니면
◦
LEFT OUTER JOIN 처리
◦
모든 필드를 SELECT해서 엔티티로 조회한 다음에 계산
정리
•
프로젝션 대상이 ROOT 엔티티 일 경우 유용하다.
•
프로젝션 대상이 ROOT 엔티티 를 넘어가면 JPQL SELECT 최적화가 안된다.
•
실무에서는 단순한 경우에만 사용한다.
Native Query
→ 가급적 네이티브 쿼리는 사용하지 않는게 좋다. 정말 어쩔수 없을 때 사용. 최신 버전에서는 스프링 데이터 Projections 가 지원되니 이 기능을 사용하도록 하자.
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);
Java
복사
동적 네이티브 쿼리
•
하이버네이트를 직접 활용한다.
•
스프링 JdbcTemplate, MyBatis, jooq같은 외부 라이브러리를 사용한다.
ex: 하이버네이트 기능 사용
String sql = "select m.username as username from member m";
List<MemberDto> result = em.createNativeQuery(sql)
.setFirstResult(0)
.setMaxResults(10)
.unwrap(NativeQuery.class)
.addScalar("username")
.setResultTransformer(Transformers.aliasToBean(MemberDto.class))
.getResultList();
Java
복사