스프링 데이터 JPA - 나머지 기능들

HOONEY·2022년 6월 10일
0

Java

목록 보기
19/20
post-thumbnail

김영한님의 실전! 스프링 데이터 JPA 정리

Specifications(명세)

  • 도메인 주도 설계(Domain Driven Design)는 SPECIFICATION(명세)라는 개념을 소개
    스프링 데이터 JPA는 JPA Criteria를 활용해서 이 개념을 사용할 수 있도록 지원

술어(predicate) -> 실무 사용 X

  • 참 또는 거짓으로 평가
  • AND OR 같은 연산자로 조합해서 다양한 검색조건을 쉽게 생성
  • 예) 검색 조건 하나하나
  • 스프링 데이터 JPA는 org.springframework.data.jpa.domain.Specification 클래스로 정의
참고 Querydsl 사용!

Query By Example

  • 실무에서 사용하기에 매칭 조건이 너무 단순함.
  • LEFT 조인이 안됨.
  • 실무에서는 Querydsl 사용!

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

  • 인터페이스가 아닌 구체적인 DTO 형식도 가능
  • 생성자의 파라미터 이름으로 매칭
클래스 생성
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();
 	}
}

주의

  • 프로젝션 대상이 root 엔티티면, JPQL SELECT 절 최적화 가능
  • 프로젝션 대상이 ROOT가 아니면 LEFT OUTER JOIN 처리
  • 모든 필드를 SELECT해서 엔티티로 조회한 다음에 계산

정리

  • 프로젝션 대상이 root 엔티티면 유용하다.
  • 프로젝션 대상이 root 엔티티를 넘어가면 JPQL SELECT 최적화가 안된다!
  • 실무의 복잡한 쿼리를 해결하기에는 한계가 있다.
  • 실무에서는 단순할 때만 사용하고, 조금만 복잡해지면 QueryDSL을 사용하자

네이티브 쿼리

  • 가급적 네이티브 쿼리는 사용하지 않는게 좋음
  • 어쩔 수 없을 경우 스프링 데이터 Projections 사용

스프링 데이터 JPA 기반 네이티브 쿼리

  • 페이징 지원
  • 반환 타입
  1. Object[]
  2. Tuple
  3. DTO(스프링 데이터 인터페이스 Projections 지원)
  • 제약
  1. Sort 파라미터를 통한 정렬이 정상 동작하지 않을 수 있음(믿지 말고 직접 처리)
  2. JPQL처럼 애플리케이션 로딩 시점에 문법 확인 불가
  3. 동적 쿼리 불가

결론

  • 네이티브 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);

동적 네이티브 쿼리

  • 하이버네이트를 직접 활용
  • 스프링 JdbcTemplate, myBatis, jooq같은 외부 라이브러리 사용
profile
기록하는 블로그

0개의 댓글