책 도메인 주도 설계에서 소개하는 SPECIFICATION 개념은 하위 시스템 구조와 관계없이 where 조건식이나 조인 등의 조건을 코드로 repository 단에 주면 결과를 반환하는 것을 추상화한 것이다.
스프링 데이터 JPA는 JPA Critera 를 활용하여 이 개념을 사용할 수 있도록 지원한다.
사용방법
사용하고자 하는 repository 에서 JpaSpecificationExecutor를 상속한다.
원하는 조건을 담은 Specfication 객체를 반환하는 코드를 작성한다.
해당 Specification 객체를 repository 에 넘겨 결과를 반환받는다.
참고.
실무에서는 JPA Criteria 는 복잡하여 대신 QueryDSL 을 주로 사용한다.
비영속 엔티티 객체를 통해 실제 데이터베이스에 있는 데이터를 가져올 수 있도록 해주는 방법
장점
동적 쿼리를 편하게 처리할 수 있다.
도메인 객체를 그대로 사용하여 직관적이다.
RDB에서 NOSQL로 변경해도 코드 변경이 없도록 추상화되어 있다.
JpaRepository 만 상속하면 기본적으로 제공되는 기능이다.
단점
inner 조인은 지원하지만, outer 조인을 지원하지 않는다.
복잡한 매칭 조건을 지원하지 않는다. (문자는 starts/contains/ends/regex, 다른 속성은 equals 만 지원)
중첩 제약조건을 지원하지 않는다. (ex. name = ?0 or (name = ?1 and age = ?2))
사용방법
Example.of 메서드에 비영속 엔티티객체를 넣으면 Example 인스턴스를 생성하여 반환한다.
Example 인스턴스를 repositoy 메서드에 인자로 넘겨 사용한다.
특정 필드만 비교하여 데이터를 가져오고 싶다면 ExampleMatcher 인스턴스를 생성하여 Example 인스턴스 생성 시 인자로 넘겨 사용할 수 있다.
엔티티 대신에 DTO를 편리하게 조회할 때 사용
Closed Projection
인터페이스를 새로 정의한 뒤 필요한 필드에 대해 getter 를 정의한다.
JpaRepository 를 상속하는 인터페이스에서 새로 추가한 인터페이스를 반환타입으로 갖는 새로운 메서드를 정의한다. (ex. List<UsernameOnly> findAll())
반환타입 인터페이스에 getter가 정의된 데이터만 가져온다.
Open Projection
위와 마찬가지로 인터페이스를 새로 정의하고 메서드를 정의하는데 필드명으로 getter 를 만드는 것이 아니라, @Value 어노테이션을 붙여 어떤 값을 어떻게 가져올 지 알려준다.
위와 같이 repository 에 새로운 메서드를 정의한다.
엔티티 전체를 조회한 뒤, 해당 데이터만 추출하여 사용할 수 있게 한다.
Class Projection
반환 타입 클래스를 정의하고 필드들을 정의한 뒤 생성자도 정의한다.
생성자에 파라미터명을 분석하여 JPA 가 쿼리를 날려 해당 값들을 가져와 생성자를 통해 생성하여 반환한다.
생성자 파라미터로 정의한 데이터만 가져온다.
추가. repository 에 Class 타입을 파라미터로 추가로 갖는 메서드를 정의하면 같은 쿼리를 다양한 반환타입으로 가져올 수 있도록 정의할 수 있다.
Nested Projection
연관관계에 있는 엔티티에서도 값을 추출하여 반환할 수 있도록 하는 기능
인터페이스를 새로 정의한 뒤, 연관관계로 묶인 엔티티에 getter 를 작성하고, 내부 인터페이스를 정의하여 해당 연관관계에 있는 엔티티에서 어떤 필드를 가져올 것인지 getter 를 작성하여 사용
root 엔티티는 쿼리 실행 최적화로 getter 에 명시된 데이터만 불러오지만, 내부 인터페이스의 getter 까지 인식하여 최적화하지는 못함.
LEFT OUTER JOIN 을 통해 연관관계 엔티티를 조회함
정리
간단하게 사용하기엔 좋으나, 복잡한 쿼리를 사용하거나, 조인이 발생하면 QueryDSL 사용이 낫다.
스프링 데이터 JPA에서는 @Query value 값으로 네이티브 쿼리를 작성하고, nativeQuery 값으로 true 를 설정하면 쉽게 네이티브 쿼리를 작성할 수 있다.
반환 타입으로 Projections 를 지원한다.
페이징도 가능하다.
제약
Sort 파라미터를 통한 정렬이 항상 동작하지 않을 수 있다.
동적 쿼리가 불가능하다.
애플리케이션 로딩 시점에 문법을 확인할 수 없다.