org.springframework.data.jpa.domain.Specification
Spring Data JPA에서 Specification은 repository 계층에서 복잡한 쿼리 조건을 생성하고 조합하기 위한 인터페이스
JPQL 을 직접 작성하지 않고도 동적으로 쿼리 구성 가능
여러개의 필드에 따른 복잡한 쿼리 조건으로 데이터를 조회하고자 할 때 findAll(Specification spec)
과 같은 메서드와, JpaSpecificationExecutor
인터페이스를 사용하여 데이터 동적 필터링 가능
@Repository
public interface ProductRepository extends JpaRepository<Product, String>, JpaSpecificationExecutor<Product> {
// specification 사용
Page<Product> findAll(Specification<Product> spec, Pageable pageable);
Page<Product> findByType(ProductType type, Pageable pageable);
//...
}
public class ProductSpecification {
public static Specification<Product> withDynamicQuery(ProductType type, ProductListReq productListReq) {
return (root, query, builder) -> {
List<Predicate> predicates = new ArrayList<>();
if (type != null) {
predicates.add(builder.equal(root.get("type"), type));
}
if (productListReq != null && productListReq.getSearchKeyWord() != null
&& !productListReq.getSearchKeyWord().isEmpty()) {
switch (productListReq.getSearchType()) {
case PRODUCT_NAME:
predicates.add(builder.like(root.get("name"), "%" + productListReq.getSearchKeyWord() + "%"));
break;
case CATEGORY_NAME:
predicates.add(builder.like(root.get("category").get("name"),
"%" + productListReq.getSearchKeyWord() + "%"));
break;
case STOCK:
predicates.add(
builder.equal(root.get("stock"), Integer.parseInt(productListReq.getSearchKeyWord())));
break;
case PRICE:
predicates.add(
builder.equal(root.get("price"), new BigDecimal(productListReq.getSearchKeyWord())));
break;
case DESCRIPTION:
predicates.add(
builder.like(root.get("description"), "%" + productListReq.getSearchKeyWord() + "%"));
break;
}
}
return builder.and(predicates.toArray(new Predicate[0]));
};
}
}
type의 개수 * searchType의 종류
만큼의 JPQL을 생성해야 했지만, 이를 하나씩 다 만들지 않고도 단 하나의 JPQL만으로 동적 쿼리들을 수행할 수 있어서 편리하고 유지보수에 용이하다고 생각했다!
처음보는 JPA 기능이네요! 유용해보여요