Chapter 5: 스프링 데이터 JPA를 이용한 조회 기능
💡 책에서 기억하고 싶은 내용
검색을 위한 스펙
스펙 (Specification)
검색 조건을 다양항게 조합해야 할 때 사용할 수 있는 것
애그리거트가 특정 조건을 충족하는지를 검사할 때 사용하는 인터페이스
ex)
isSatisfiedBy()
method의 agg 파라미터는검사 대상
이 되는 객체다스펙을
리포지터리
에 사용하면 agg는애그리거트 루트
가 되고,스펙을
DAO
에 사용하면 agg는검색 결과
로 리턴할 데이터 객체가 된다
스프링 데이터 JPA를 이용한 스펙 구현
스프링 데이터 JPA는
검색 조건
을 표현하기 위한 인터페이스인Specification
을 제공한다스펙 인터페이스는
함수형 인터페이스
이므로 람다식을 이용해서 객체를 생성할 수 있다
JPA 정적 메타 모델
정적 메타 모델은
@StaticMetamodel
annotation을 이용해서 관련 모델을 지정한다메타 모델 클래스는 모델 클래스의 이름 뒤에 ‘_’ 을 붙인 이름을 갖는다
정적 메타 모델 클래스는 대상 모델의 각 프로퍼티와 동일한 이름을 갖는
정적 필드
를 정의한다이 정적 필드는 프로퍼티에 대한 메타 모델로서, 프로퍼티 타입에 따라
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 타입 객체는PageRequest
class를 이용해서 생성한다ex)
PageRequest.of()
method의 첫 번째 인자는페이지 번호
를, 두 번째 인자는한 페이지의 개수
를 의미한다페이지 번호는 0번부터 시작하므로, 위 코드는 한 페이지에 10개씩 표시한다고 했을 때 두번째 페이지를 조회한다.
즉, 11번째부터 20번째까지 데이터를 조회한다
PageRequest
와Sort
를 사용하면 정렬 순서를 지정할 수 있다ex)
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