10분끄적임

JPA Specifications 가볍게 살펴보기

snaplogger 2022. 12. 12. 08:30

스프링 Data JPA에서 복잡한 조건으로 쿼리를 수행하기 위해서 보통은 JPQL 또는 SQL(= Native Query)를 많이 사용합니다. 이 번엔 새로운 방법인 Specifications를 알아보겠습니다.

 

함께 살펴볼 'Spring Data JPA 공식 문서' 링크입니다.

https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#specifications

 

Spring Data JPA - Reference Documentation

Example 119. Using @Transactional at query methods @Transactional(readOnly = true) interface UserRepository extends JpaRepository { List findByLastname(String lastname); @Modifying @Transactional @Query("delete from User u where u.active = false") void del

docs.spring.io

우선, 기존 CrudRepository에 추가로 JpaSpecificationExecutor를 상속받아야 합니다.

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

JpaSpecificationExecutor를 상속받게 되면 다음과 같은 형태의 메서드가 추가되어 있음을 확인할 수 있습니다.

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

 

Specification 정의하기

Specification 인터페이스는 다음과 같이 생겼습니다.

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

그리고 각각의 인자 root, builder의 사용법은 예제를 먼저 살펴보겠습니다.

 

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
    };
  }
}

우선 root는 해당 객체 여기서는 'Customer' 객체를 의미합니다.

아래와 같은 코드로 현재 'Customer' 객체의 createdAt 값을 읽어올 수 있습니다.

root.get(Customer_.createdAt)
// 또는
root.get("createdAt")

builder로는 조건문을 완성할 수 있습니다.

builder.lessThan(root.get(Customer_.createdAt), date)

이 두 가지를 이용해서 18살 이하의 고객을 구하는 Specification을 정의해보면 다음과 같습니다.

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

그리고 해당 Sepcification을 이용해서 쿼리를 실행할 수 있게 됩니다.

customerRepository.findAll(ageLessThan18)

 

 

다음엔 두 번째 인자인 CriteriaQuery<?> query에 대하여 알아보도록 하겠습니다. 👋

 

2022.11.24 - [10분끄적임] - '10분 끄적임' 카테고리를 만들었어요