728x90
반응형
Specifications (명세)
언어에 상관없이 조건을 조립해서 쓸 수 있도록 만든 추상화한 개념
Predicate (술어)
- 참 또는 거짓으로 평가
- AND, OR 같은 연산자로 조합해서 다양한 검색조건을 쉽게 생성 (컴포지트 패턴)
- 스프링 데이터 JPA는
org.springframework.data.jpa.domain.Specification
클래스로 정의
사용 방법
JpaSpecificationExecutor 인터페이스 상속
public interface MemberRepository extends JpaRepository<Member, Long>, JpaSpecificationExecutor<Member> {}
JpaSpecificationExecutor 인터페이스
public interface JpaSpecificationExecutor<T> {
Optional<T> findOne(@Nullable Specification<T> spec);
List<T> findAll(Specification<T> spec);
Page<T> findAll(Specification<T> spec, Pageable pageable);
List<T> findAll(Specification<T> spec, Sort sort);
long count(Specification<T> spec);
}
- Specification을 파라미터로 받아 검색조건으로 사용
where(), and(), or(), not()
을 제공한다.
※ 자바 코드로 동적 쿼리를 만들 수 있다는 장점이 있지만 사용하기 너무 어렵고 직관적이지 않다는 단점이 있다.
Query By Example
단순한 인터페이스를 통해 동적으로 쿼리를 만드는 사용자 친화적인 기술로 어떠한 객체(엔티티)를 갖고 쿼리를 만드는 개념
용어
Probe
: 필드에 데이터가 있는 실제 도메인 객체ExampleMatcher
: 특정 필드를 일치시키는 상세한 정보(포함, 예외) 제공, 재사용 가능Example
:Probe
와ExampleMatcher
로 구성되어 쿼리를 생성하는데 사용
장점
- 동적 쿼리를 편리하게 처리
- 도메인 객체를 그대로 사용
- 데이터 저장소를 RDB에서 NoSQL로 변경해도 코드 변경이 없게 추상화 되어 있음
- 스프링 데이터 JPA 인터페이스 포함
단점
- Inner Join은 가능하지만 Outer Join은 불가
A = ?0 OR (A = ?1 AND B = ?2)
와 같은 중첩 조건 설정이 불가- 매칭 조건이 매우 단순
- 문자는 starts/contains/ends/regex
- 다른 속성은 일치 '=' 만 지원
※ 매칭 조건이 단순하고 외부 조인이 불가능하여 실무에서는 잘 사용하지 않는다.
Projections
쿼리 SELECT절에 들어갈 데이터를 지정하는 것으로 엔티티 대신 DTO를 편리하게 조회할 때 사용한다.
인터페이스 기반 Closed Projections
public interface UsernameOnly {
String getUsername();
}
- 프로퍼티 형식(Getter)의 인터페이스를 제공하면 구현체는 스프링 데이터 JPA가 제공
- 정확히 매칭하여 값을 가져오기 때문에 SELECT절 최적화
인터페이스 기반 Open Projections
public interface UsernameOnly {
@Value("#{target.username + ' ' + target.age + ' ' + target.team.name}")
String getUsername();
}
@Value
어노테이션과 스프링의 SpEL 문법 사용하여 구현- DB에서 엔티티 필드를 다 조회해 온 후 계산하기 때문에 SELECT절 최적화가 잘 안된다.
클래스 기반 Projection
public class UsernameOnlyDto {
private final String username;
public UsernameOnlyDto(String username) {
this.username = username;
}
public String getUsername() {
return username;
}
}
- 인터페이스가 아닌 DTO형식으로 프로젝션 가능
- 생성자의 파라미터 이름으로 매칭
동적 Projection
<T> List<T> findProjectionsByUsername(String username, Class<T> type);
- Generic Type을 주면 동적으로 프로젝션 데이터 변경 가능
중첩 구조 처리
public interface NestedClosedProjection {
String getUsername();
TeamInfo getTeam();
interface TeamInfo {
String getName();
}
}
- 프로젝션 대상이 ROOT 엔티티면 JPQL SELECT절 최적화가 가능하다.
- 프로젝션 대상이 ROOT 엔티티가 아니면 Left Outer Join이 되고 모든 필드를 SELECT 해서 엔티티로 조회한 이후 계산하기 때문에 SELECT절 최적화가 안된다.
정리
- 프로젝션 대상이 ROOT 엔티티면 유용하다.
- 프로젝션 대상이 ROOT 엔티티를 넘어가면 JPQL SELECT 최적화가 안된다.
- 실무의 복잡한 쿼리를 해결하기엔 한계가 있다.
- 실무에서는 단순할 때만 사용하는 것이 좋다.
네이티브 쿼리
JPA가 제공하는 기능으로 JDBC, JdbcTemplate, MyBatis 등을 사용하여 SQL을 직접 작성하여 사용하는 것으로 JPA를 사용한다면 가급적 네이티브 쿼리를 사용하지 말아야 하며 정말 최후의 방법으로 사용해야 한다.
스프링 데이터 JPA 기반 네이티브 쿼리
- 페이징 지원
- 반환 타입
-Object[]
- Tuple
- DTO(스프링 데이터 인터페이스 Projections 지원) - 제약
- Sort 파라미터를 통한 정렬이 정상 동작하지 않을 수 있어 직접 처리하는 것을 권장한다.
- JPQL처럼 애플리케이션 로딩 시점에 문법 확인 불가
- 동적 쿼리 불가
JPA 네이티브 SQL 지원
public interface MemberRepository extends JpaRepository<Member, Long> {
@Query(value = "select * from member where username = ?", nativeQuery = true)
Member findByNativeQuery(String username);
}
- JPQL은 위치 기반 파라미터가 1부터 시작하지만 네이티브 SQL은 0부터 시작
- 네이티브 SQL을 엔티티가 아닌 DTO로 변환하기 위해선
- DTO 대신 JPA Tuple 조회
- DTO 대신 Map 조회
-@SqlResultSetMapping
사용
- Hibernate ResultTransformer 사용
- 네이티브 SQL을 DTO로 조회할 때는 JdbcTemplate or MyBatis 권장
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);
- 스프링 데이터 JPA 네이티브 쿼리 + 인터페이스 기반 Projections 활용
동적 네이티브 쿼리
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();
- 하이버네이트 직접 활용
- 스프링 JdbcTemplate, MyBatis, jooq 같은 외부 라이브러리 사용
728x90
반응형
'Dev > Spring Data JPA' 카테고리의 다른 글
[Spring Data JPA] 스프링 데이터 JPA 분석 (0) | 2021.12.14 |
---|---|
[Spring Data JPA] 확장 기능 (0) | 2021.12.13 |
[Spring Data JPA] 쿼리 메소드 기능 (0) | 2021.12.09 |
[Spring Data JPA] 공통 인터페이스 기능 (0) | 2021.11.29 |
[Spring Data JPA] 예제 도메인 모델 (0) | 2021.11.29 |
댓글