Specifications

Dev.Hammy·2024년 4월 21일
0

Spring Data JPA

목록 보기
10/13

JPA 2에는 프로그래밍 방식으로 쿼리를 작성하는 데 사용할 수 있는 기준 API가 도입되었습니다. criteria을 작성하여 도메인 클래스에 대한 쿼리의 where 절을 정의합니다. 한 걸음 더 물러서면 이러한 기준은 JPA 기준 API 제약 조건에 의해 설명되는 엔터티에 대한 조건자로 간주될 수 있습니다.

Spring Data JPA는 Eric Evans의 저서 "Domain Driven Design"에서 사양 개념을 가져와 동일한 의미론을 따르고 JPA 기준 API를 사용하여 이러한 사양을 정의하는 API를 제공합니다. 사양을 지원하려면 다음과 같이 JpaSpecificationExecutor 인터페이스를 사용하여 저장소 인터페이스를 확장할 수 있습니다.

public interface CustomerRepository extends CrudRepository<Customer, Long>, JpaSpecificationExecutor<Customer> {}

추가 인터페이스에는 다양한 방식으로 사양을 실행할 수 있는 메서드가 있습니다. 예를 들어 findAll 메소드는 다음 예와 같이 사양과 일치하는 모든 엔터티를 반환합니다.

List<T> findAll(Specification<T> spec);

Specification 인터페이스는 다음과 같이 정의됩니다.

public interface Specification<T> {
  Predicate toPredicate(Root<T> root, CriteriaQuery<?> query,
            CriteriaBuilder builder);
}

사양은 다음 예에 표시된 것처럼 필요한 모든 조합에 대해 쿼리(메서드)를 선언할 필요 없이 JpaRepository와 결합하여 사용할 수 있는 엔터티 위에 확장 가능한 predicate 세트를 구축하는 데 쉽게 사용할 수 있습니다.

Example 1. Specifications for a Customer

public class CustomerSpecs {


  public static Specification<Customer> isLongTermCustomer() {
    return (root, query, builder) -> {
      LocalDate date = LocalDate.now().minusYears(2);
      return builder.lessThan(root.get(Customer_.createdAt), date);
    };
  }

  public static Specification<Customer> hasSalesOfMoreThan(MonetaryAmount value) {
    return (root, query, builder) -> {
      // build query here
    };
  }
}

Customer_ 유형은 JPA Metamodel 생성기를 사용하여 생성된 메타모델 유형입니다(예제는 Hibernate 구현 문서 참조). 따라서 Customer_.createdAt 표현식은 CustomerDate 유형의 createAt 속성을 가지고 있다고 가정합니다. 그 외에도 비즈니스 요구 사항 추상화 수준에 대한 몇 가지 기준을 표현하고 실행 가능한 Specifications을 만들었습니다. 따라서 클라이언트는 다음과 같이 Specification을 사용할 수 있습니다.

Example 2. Using a simple Specification

List<Customer> customers = customerRepository.findAll(isLongTermCustomer());

이런 종류의 데이터 액세스에 대한 쿼리를 만들어 보는 것은 어떨까요? 단일 Specification을 사용하면 일반 쿼리 선언에 비해 많은 이점을 얻지 못합니다. Specification의 힘은 사양을 결합하여 새로운 Specification 개체를 만들 때 실제로 빛납니다. 다음과 유사한 표현식을 작성하기 위해 우리가 제공하는 Specification의 기본 method을 통해 이를 달성할 수 있습니다.

Example 3. Combined Specifications

MonetaryAmount amount = new MonetaryAmount(200.0, Currencies.DOLLAR);
List<Customer> customers = customerRepository.findAll(
  isLongTermCustomer().or(hasSalesOfMoreThan(amount)));

SpecificationSpecification 인스턴스를 연결하고 결합하기 위한 몇 가지 "접착 코드" 기본 방법을 제공합니다. 이러한 방법을 사용하면 새로운 Specification 구현을 생성하고 이를 기존 구현과 결합하여 데이터 액세스 계층을 확장할 수 있습니다.

그리고 JPA 2.1에서는 CriteriaBuilder API에 CriteriaDelete가 도입되었습니다. 이는 JpaSpecificationExecutordelete(Specification) API를 통해 제공됩니다.

Example 4. Using a Specification to delete entries.

Specification<User> ageLessThan18 = (root, query, cb) -> cb.lessThan(root.get("age").as(Integer.class), 18)

userRepository.delete(ageLessThan18);

Specificationage 필드(정수로 변환)가 18 미만인 기준을 구축합니다. userRepository에 전달되면 JPA의 CriteriaDelete 기능을 사용하여 올바른 DELETE 작업을 생성합니다. 그런 다음 삭제된 엔터티 수를 반환합니다.

0개의 댓글