Chapter 5: 스프링 데이터 JPA를 이용한 조회 기능
💡 책에서 기억하고 싶은 내용
검색을 위한 스펙
스펙 (Specification)검색 조건을 다양항게 조합해야 할 때 사용할 수 있는 것
애그리거트가 특정 조건을 충족하는지를 검사할 때 사용하는 인터페이스
ex)
public interface Specification<T> { public boolean isSatisfiedBy(T agg); }isSatisfiedBy()method의 agg 파라미터는검사 대상이 되는 객체다스펙을
리포지터리에 사용하면 agg는애그리거트 루트가 되고,스펙을
DAO에 사용하면 agg는검색 결과로 리턴할 데이터 객체가 된다
스프링 데이터 JPA를 이용한 스펙 구현
스프링 데이터 JPA는
검색 조건을 표현하기 위한 인터페이스인Specification을 제공한다스펙 인터페이스는
함수형 인터페이스이므로 람다식을 이용해서 객체를 생성할 수 있다
JPA 정적 메타 모델
정적 메타 모델은
@StaticMetamodelannotation을 이용해서 관련 모델을 지정한다메타 모델 클래스는 모델 클래스의 이름 뒤에 ‘_’ 을 붙인 이름을 갖는다
정적 메타 모델 클래스는 대상 모델의 각 프로퍼티와 동일한 이름을 갖는
정적 필드를 정의한다이 정적 필드는 프로퍼티에 대한 메타 모델로서, 프로퍼티 타입에 따라
SingularAttribute,ListAttribute등의 타입을 사용해서 메타 모델을 정의한다
정적 메타 모델 클래스를 직접 작성할 수 있지만, Hibernate와 같은 JPA Provider는 정적 메타 모델을 생성하는 도구를 제공하고 있으므로 이들 도구를 사용하면 편리하다
리포지터리 / DAO에서 스펙 사용하기
스펙을 충족하는 엔티티들 검색하고 싶다면,
findAll()method를 사용하면 된다스펙 객체를 생성하고
findAll()method를 이용해서 검색findAll()method는스펙 인터페이스를 파라미터로 갖는다
스펙 조합
스프링 데이터 JPA가 제공하는 스펙 인터페이스는 스펙을 조합할 수 있는 두 method를 제공하고 있다
and()와or()
and()와or()method는 기본 구현을 가진 default method 이다and()method는 두 스펙을 모두 충족하는 조건을 표현하는 스펙을 생성하고,or()method는 두 스펙 중 하나 이상 충족하는 조건을 표현하는 스펙을 생성한다
정렬 지정하기
스프링 데이터 JPA는 두 가지 방법을 이용해서 정렬을 지정할 수 있다
method 이름에
OrderBy를 사용해서 정렬 기준 지정Sort를 인자로 전달
페이징 처리하기
스프링 데이터 JPA는 페이징 처리를 위해
Pageable타입을 이용한다Sort타입과 마찬가지로, find() method에Pageable타입 파라미터를 사용하면 페이징을 자동으로 처리해 준다Pageable타입은인터페이스로, 실제 Pageable 타입 객체는PageRequestclass를 이용해서 생성한다ex)
import org.springframework.data.domain.PageRequest PageRequest pageReq = PageRequest.of(1, 10); List<MemberData> user = memberDataDao.findByNameLike("chloe%", pageReq);PageRequest.of()method의 첫 번째 인자는페이지 번호를, 두 번째 인자는한 페이지의 개수를 의미한다페이지 번호는 0번부터 시작하므로, 위 코드는 한 페이지에 10개씩 표시한다고 했을 때 두번째 페이지를 조회한다.
즉, 11번째부터 20번째까지 데이터를 조회한다
PageRequest와Sort를 사용하면 정렬 순서를 지정할 수 있다ex)
Sort sort = Sort.by("name").descending(); PageRequest pageReq = PageRequest.of(1, 2, sort); List<MemberData> user = memberDataDao.findByNameLike("chloe%", pageReq);
Pageable을 사용하는 method의 return type이Page일 경우, 스프링 데이터 JPA는 목록 조회 쿼리와 함께COUNT쿼리도 실행해서 조건에 해당하는 데이터 개수를 구한다Page는 전체 개수, 페이지 개수 등 페이징 처리에 필요한 데이터도 함께 제공한다Page가 제공하는 method 다수 있음!
프로퍼티를 비교하는
findBy프로퍼티()형식의 method는Pagable타입을 사용하더라도 리턴 타입이List면 COUNT 쿼리를 실행하지 않는다페이징 처리와 관련된 정보가 필요 없다면, Page 리턴 타입이 아닌 List를 사용해서 불필요한 COUNT 쿼리를 실행하지 않도록 한다
반면,
스펙을 사용하는findAll()method에Pageable타입을 사용하면, 리턴 타입이 Page가 아니어도 COUNT 쿼리를 실행한다즉, 페이지 관련 정보가 필요 없더라도 COUNT 쿼리를 실행한다
만약 처음부터 N개의 데이터가 필요하다면,
Pageable을 사용하지 않고findFirstN()형식의 method를 사용할 수도 있다First대신Top을 사용해도 된다First나Top뒤에 숫자가 없으면 한 개 결과만 리턴한다
스펙 조합을 위한 스펙 빌더 클래스
스펙을 사용하다보면 조건에 따라 스펙을 조합해야 할 때가 있다
그 때
스팩 빌더를 만들어 사용한다
스펙 빌더에는
and(),ifHasText(),ifTrue()method가 있는데, 이 외에 필요한 method를 추가해서 사용하면 된다
동적 인스턴스 생성
JPA는 쿼리 결과에서 임의의 객체를
동적으로 생성할 수 있는 기능을 제공하고 있다JPQL의 select 절에
new키워드를 사용하고,new키워드 뒤에 생성할 인스턴스의 완전한 클래스 이름을 지정하고 괄호 안에 생성자에 인자로 전달할 값을 지정한다동적 인스턴스의 장점JPQL을 그대로 사용하므로
객체 기준으로 쿼리를 작성하면서도, 동시에지연/즉시 로딩과 같은 고민 없이 원하는 모습으로 데이터를 조회할 수 있다
하이버네이트 @Subselect 사용
@Subselect 사용하이버네이트는 JPA 확장 기능으로
@Subselect를 제공한다@Subselect쿼리 결과를@Entity로 매핑해준다
@Immutable,@Subselect,@Synchronize는 하이버네이트 전용 annotation인데 이 태그를 사용하면 테이블이 아닌쿼리 결과를@Entity로 매핑할 수 있다@Subselect는 SELECT 쿼리를 값으로 갖는다하이버네이트는 이 SELECT 쿼리의 결과를 매핑할 테이블처럼 사용한다
DBMS가 여러 테이블을 조인해서 조회한 결과를 한 테이블처럼 보여주기 위한 용도로 뷰를 사용하는 것처럼,
@Subselect를 사용하면쿼리 실행 결과를매핑할 테이블처럼 사용한다
@Immutable뷰를 수정할 수 없듯이
@Subselect로 조회한@Entity역시 수정할 수 없다실수로
@Subselect를 이용한@Entity의 매핑 필드를 수정하면, 하이버네이트는 변경 내역을 반영하는 UPDATE 쿼리를 실행한다But, 매핑 한 테이블이 없으므로 에러가 발생한다
이런 문제를 방지하기 위해
@Immutable을 사용한다@Immutable을 사용하면 하이버네이트는 해당 엔티티의 매핑 필드 / 프로퍼티가 변경되어도 DB에 반영하지 않고 무시한다
@Synchronize@Synchronize는 해당 엔티티와 관련된 테이블 목록을 명시한다하이버네이트는 엔티티를 로딩하기 전에 지정한 테이블과 관련된 변경이 발생하면 FLUSH를 먼저 한다
@Synchronize가 지정하고 있는 테이블에 변경이 발생하면, 관련 내역을 먼저 FLUSH 한다따라서 해당 class를 로딩하는 시점에서는 변경된 내역이 반영된다
@Subselect@Subselect를 사용해도 일반@Entity와 같기 때문에EntityManager#find(),JPQL,Criteria를 사용해서 조회할 수 있다는 것이@Subselect의 장점이다스펙도 사용할 수 있다
@Subselect는 이름처럼@Subselect의 값으로 지정한 쿼리를 FROM 절의서브 쿼리로 사용한다서브 쿼리를 사용하고 싶지 않다면, 네이티브 SQL 쿼리를 사용하거나 MyBatis 와 같은 별도
mapper를 사용해서 조회 기능을 구현해야 한다
Last updated
Was this helpful?