김영한님의 실전! 스프링 데이터 JPA 정리
1. Specifications(명세)
- 도메인 주도 설계(Domain Driven Design)는 SPECIFICATION(명세)라는 개념을 소개
- 스프링 데이터 JPA는 JPA Criteria를 활용해서 이 개념을 사용할 수 있도록 지원
2. 술어(predicate) -> 실무 사용 X
- 참 또는 거짓으로 평가
- AND OR 같은 연산자로 조합해서 다양한 검색조건을 쉽게 생성
예) 검색 조건 하나하나
- 스프링 데이터 JPA는 org.springframework.data.jpa.domain.Specification 클래스로 정의
참고 Querydsl 사용!
3. Query By Example
- 실무에서 사용하기에 매칭 조건이 너무 단순함.
- LEFT 조인이 안됨.
- 실무에서는 Querydsl 사용!
4. Projections
- 엔티티 대신에 DTO를 편리하게 조회할 때 사용
- 전체 엔티티의 필드를 getter 형식으로 지정하면 해당 필드만 선택해서 조회(Projection)
- 인터페이스 생성
public interface UsernameOnly {
String getUsername();
}
- Repository에 메소드 추가
public interface MemberRepository ... {
List<UsernameOnly> findProjectionsByUsername(String username);
}
- 테스트 코드
@Test
public void projections() throws Exception {
//given
Team teamA = new Team("teamA");
em.persist(teamA);
Member m1 = new Member("m1", 0, teamA);
Member m2 = new Member("m2", 0, teamA);
em.persist(m1);
em.persist(m2);
em.flush();
em.clear();
//when
List<UsernameOnly> result = memberRepository.findProjectionsByUsername("m1");
//then
Assertions.assertThat(result.size()).isEqualTo(1);
}
- 인터페이스 기반 Closed Projections
프로퍼티 형식(getter)의 인터페이스를 제공하면, 구현체는 스프링 데이터 JPA가 제공
public interface UsernameOnly {
String getUsername();
}
- 인터페이스 기반 Open Proejctions
스프링의 SpEL 문법도 지원
public interface UsernameOnly {
@Value("#{target.username + ' ' + target.age + ' ' + target.team.name}")
String getUsername();
}
단! 이렇게 SpEL문법을 사용하면, DB에서 엔티티 필드를 다 조회해온 다음에 계산한다! 따라서 JPQL
SELECT 절 최적화가 안된다.
- 클래스 기반 Projection
(1) 인터페이스가 아닌 구체적인 DTO 형식도 가능
(2) 생성자의 파라미터 이름으로 매칭
> 클래스 생성
public class UsernameOnlyDto {
private final String username;
public UsernameOnlyDto(String username) {
this.username = username;
}
public String getUsername() {
return username;
}
}
> 동적 Projections
* Generic type을 주면, 동적으로 프로젝션 데이터 번경 가능
* Repository 코드
<T> List<T> findProjectionsByUsername(String username, Class<T> type);
> 테스트 코드
List<UsernameOnly> result = memberRepository.findProjectionsByUsername("m1", UsernameOnly.class);
- 중첩 구조 처리
인터페이스 생성
public interface NestedClosedProjection {
String getUsername();
TeamInfo getTeam();
interface TeamInfo {
String getName();
}
}
- 주의
(1) 프로젝션 대상이 root 엔티티면, JPQL SELECT 절 최적화 가능
(2) 프로젝션 대상이 ROOT가 아니면 LEFT OUTER JOIN 처리
(3) 모든 필드를 SELECT해서 엔티티로 조회한 다음에 계산
- 정리
(1) 프로젝션 대상이 root 엔티티면 유용하다.
(2) 프로젝션 대상이 root 엔티티를 넘어가면 JPQL SELECT 최적화가 안된다!
(3) 실무의 복잡한 쿼리를 해결하기에는 한계가 있다.
(4) 실무에서는 단순할 때만 사용하고, 조금만 복잡해지면 QueryDSL을 사용하자
5. 네이티브 쿼리
- 가급적 네이티브 쿼리는 사용하지 않는게 좋음
- 어쩔 수 없을 경우 스프링 데이터 Projections 사용
- 스프링 데이터 JPA 기반 네이티브 쿼리
(1) 페이징 지원
(2) 반환 타입
> Object[]
> Tuple
> DTO(스프링 데이터 인터페이스 Projections 지원)
(3) 제약
> Sort 파라미터를 통한 정렬이 정상 동작하지 않을 수 있음(믿지 말고 직접 처리)
> JPQL처럼 애플리케이션 로딩 시점에 문법 확인 불가
> 동적 쿼리 불가
- 결론
네이티브 SQL을 DTO로 조회할 때는 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);
- 동적 네이티브 쿼리
(1) 하이버네이트를 직접 활용
(2) 스프링 JdbcTemplate, myBatis, jooq같은 외부 라이브러리 사용
'개발 > Java&Kotlin' 카테고리의 다른 글
[Java] 자바가 확장한 객체 지향 (0) | 2022.08.04 |
---|---|
[Java] 자바와 객체 지향 (2) (0) | 2022.08.03 |
[Java] 자바와 객체 지향 (1) (2) | 2022.08.01 |
[JPA] 스프링 데이터 - 스프링 데이터 JPA 분석 (2) | 2022.08.01 |
[Spring] 프레젠테이션 층의 설계와 구현 (0) | 2022.07.28 |