반응형
JPA에서 동적 쿼리를 위한 스펙 구현
- JPA는 다양한 검색 조건 조합을 위해 CritetiaBuilder와 Predicate를 이용해서 검색 조건을 구현해야 한다.
/* JPA 리포지터리를 위한 Specification 인터페이스 */
public interface Specification<T> {
Predicagte toPredicate(Root<T> root, CriteriaBuilder cb);
}
/* 스펙 구현 */
public class OrdererSpec implements Specification<Order> {
private String ordererId;
public OrdererSpec(String ordererId) {
this.ordererId = ordererId;
}
@Overridee
public Predicate toPredicate(Root<Order> root, CriteriaBuilder cb) {
return cb.equeal(root.get(Order_.orderer).get(Orderer_.memberId).get(MemberId_.id)
, ordererId);
}
}
/* 구현한 스팩으로 데이터 조회 */
Specification<Order> ordererSpec = new OrdererSpec("madvirus");
List<Order> orders = orderRepository.findAll(ordererSpec);
스펙 생성 기능을 별도의 클래스에 구현
/* 스펙 생성 기능을 별도 클래스에 모은 예 */
public class OrderSpecs {
public static Specification<Order> orderer(String ordereId) {
return (root, cb) -> cb.equal(
root.get(Order_.orderer).get(Orderer_.memberId).get(MemberId_.id)
, ordererId);
}
public static Specification<Order between(Date from, Date to) {
return (root, cb) -> cb.between(root.get(Order_.orderDate), from, to);
}
}
/* 구현한 별도 클래스로 데이터 조회 */
Specification<Order> betweenSpec = OrderSpecs.between(formTime, toTime);
AND와 OR를 위한 JPA 스펙
- 생성자로 받은 Specification 목록을 Predicate 목록으로 변환 후 CriteriaBuilder의 and()와 or()를 사용하여 새로운 Predicate를 생성한다.
/* AND를 위한 JPA 스펙 */
public class AndSpecification<T> implements Specification<T> {
private List<Specification<T>> specs;
public AndSpecification(Specification<T> ... specs) {
this.specs = Arrays.asList(specs);
}
@Override
public Predicate toPredicate(Root<T> root, CriteriaBuilder cb) {
Predicate[] predicates = specs.stream()
.map(spec -> spec.toPredicate(root, db))
.toArray(size -> new Predicate[size]);
return cb.and(predicates);
}
}
/* OR를 위한 JPA 스펙 */
public class OrSpecification<T> implements Specification<T> {
private List<Specification<T>> specs;
public OrSpecification(Specification<T> ... specs) {
this.specs = Arrays.asList(specs);
}
@Override
public Predicate toPredicate(Root<T> root, CriteriaBuilder cb) {
Predicate[] predicates = specs.stream()
.map(spec -> spec.toPredicate(root, db))
.toArray(Predicate[]::new);
return cb.or(predicates);
}
}
AND/OR 스펙을 생성해주는 팩토리 클래스
/* AND/OR 스펙을 생성해주는 팩토리 클래스 */
public class Specs {
public static <T> Specification<T> and(Specification<T> ... specs) {
return new AndSpecification<>(specs);
}
public static <T> Specification<T> or(Specification<T> ... specs) {
return enw OrSpecification<>(specs);
}
}
/* 구현한 팩토리 클래스를 사용 */
Specification<Order> specs = Specs.and(
OrderSpecs.orderer("madvirus"), OrderSpecs.between(fromTime, toTime)
);
스펙을 사용하는 JPA 리포지터리 구현
/* 스펙을 사용하는 JPA 리포지터리 구현 */
public interface OrderRepository {
public List<Order> findAll(Specification<Order> spec);
...
}
반응형
'DDD' 카테고리의 다른 글
DDD(Domain-Driven Design) 계층구조(Layered Architecture) (0) | 2021.09.15 |
---|---|
이벤트(Event) 개요 (0) | 2021.09.15 |
도메인 영역의 주요 구성요소 (0) | 2021.09.15 |
응용(Application) 영역과 표현(UI) 영역 (0) | 2021.09.15 |
도메인(Domain) 모델에 지켜야할것! (0) | 2021.09.15 |