public interface Specification<T> {
public boolean isSatisfiedBy(T agg);
}
public class OrdererSpec implements Specification<Order> {
private String ordererId;
public boolean isSatisfiedBy(Order agg) {
return agg.getOrdererId().getMemberId().getId().equals(ordererId);
}
}
public List<Order> findAll(Specification<Order> spec) {
List<Order> allOrders = findAll();
return allOrders.stream()
.filter(order -> sepc.isSatisfiedBy(order))
.toList()
}
스펙 인터페이스에서 지네릭 타입 파라미터는 JPA의 엔티티타입을 의미, JPA 크리테리아 API에서 조건을 표현할 때 Predicate를 생성
package org.springframework.data.jpa.domain;
public interface Specification<T> extends Serializable {
@Nullable
Predicate toPredicate(Root<T> root,
CriteriaQuery<?> query,
CriteriaBuilder cb);
}
public class OrdererIdSpec implements Specification<OrderSummary> {
private String ordererId;
public OrdererIdSpec(String ordererId) {
this.ordererId = ordererId;
}
@Override
public Predicate toPredicate(Root<OrderSummary> root,
CriteriaQuery<?> query,
CriteriaBuilder cb) {
return cb.equal(root.get(OrderSummary_.ordererId), ordererId);
}
}
굳이 JPA Criteria를 사용해야할까..?
너무 복잡해 보임,
public interface OrderSummaryDao extends Repository<OrderSummary, String> {
List<OrderSummary> findAll(Specification<OrderSummary> spec);
}
// 스펙 객체 생성하고
Specification<OrderSummary> spec = new OrdererIdSpec("user1");
// findAll() 메서드를 이용해서 검색
List<OrderSummary> results = orderSummaryDao.findAll(spec);
public interface Specification<T> extends Serializable {
default Specification<T> and(@Nullable Specification<T> other) {...}
default Specification<T> or(@Nullable Specification<T> other) {...}
@Nullable
Predicate toPredicate(Root<T> root,
CriteriaQuery<?> query,
CriteriaBuilder cb);
}
}
// 조합 예시
default Specification<T> and(@Nullable Specification<T> other) { ... }
default Specification<T> or(@Nullable Specification<T> other) { ... }
static <T> Specification<T> not(@Nullable Specification<T> spec) { ... }
static <T> Specification<T> where(@Nullable Specification<T> spec) { ... }
스프링 데이터 JPA 는 두가지 방법으로 정렬을 지정할 수 있다.
OrderBy
public interface OrderSummaryDao extends Repository<OrderSummary, String> {
List<OrderSummary> findByOrdererIdOrderByNumberDesc(String ordererId);
Sort
public interface OrderSummaryDao extends Repository<OrderSummary, String> {
List<OrderSummary> findByOrdererId(String ordererId, Sort sort);
}
Sort sort1 = Sort.by("number").ascending();
Sort sort2 = Sort.by("orderDate").ascending();
Sort sort = Sort1.and(sort2);
List<OrderSummary> results = orderSummaryDao.findByOrdererId("user1",sort);
public interface MemberDataDao extends Repository<MemberData, String> {
List<MemberData> findByNameLike(String name, Pageable pageable);
}
PageRequest pageReq = PageRequest.of(1, 10);
List<MemberData> uesr = memberDataDao.findByNameLike("사용자%", pageReq);
public class SpecBuilder {
public static <T> Builder<T> builder(Class<T> type) {
return new Builder<T>();
}
public static class Builder<T> {
private List<Specification<T>> specs = new ArrayList<>();
public Builder<T> and(Specification<T> spec) {
specs.add(spec);
return this;
}
public Builder<T> ifHasText(String str,
Function<String, Specification<T>> specSupplier) {
if (StringUtils.hasText(str)) {
specs.add(specSupplier.apply(str));
}
return this;
}
public Builder<T> ifTure(Boolean cond,
Supplier<Specification<T>> specSupplier) {
if (cond != null && cond.booleanValue()) {
specs.add(specSupplier.get());
}
return this;
}
public Specification<T> toSpec() {
Specification<T> spec = Specification.where(null);
for (Specification<T> s : specs) {
spec = spec.and(s);
}
return spec;
}
}
}
JPQL : 쿼리 실행 전까지는 문법오류를 판단하지못함
Criteria : 컴파일 단계에서 문법오류는 판단할 수 있음.. 하지만 가독성이 떨어지고 코드 자체의 복잡성이 증가하는 것 같음..
QueryDSL : 내 눈에는 컴파일 단계에서 문법오류도 찾고 쿼리처럼 사용 할 수 있는 QueryDSL을 사용하는게 제일 좋아보인다....
정보에 감사드립니다.