IT서적/도메인 주도 개발 시작하기
[도메인 주도 개발 시작하기] 5. 스프링 데이터 JPA를 이용한 조회 기능
dev22
2023. 7. 28. 09:35
728x90
CORS는 명령 모델과 조회 모델을 분리하는 패턴이다. 명령 모델은 상태를 변경하는 기능을 구현(회원가입, 암호 변경, 주문 취소)할 때 사용하고 조회 모델은 데이터를 조회하는 기능을 구현(주문 목록, 주문 상세처럼 데이터를 보여주는 기능)할 때 사용한다. 이 장에서 살표볼 구현 방법은 조회 모델을 구현할 때 주로 사용한다.
검색을 위한 스펙
검색 조건을 다양하게 조합해야 할 때 사용할 수 있는 것이 스펙이다. 스펙은 애그리거트가 특정 조건을 충족하는지를 검사할 때 사용하는 인터페이스다.
public interface Specification<T> {
public boolean isSatisfiedBy(T agg);
}
스펙을 리포지터리에 사용하면 egg는 애그리거트 루트가 되고, 스펙을 DAO에 사용하면 agg는 검색 결과로 리턴할 데이터 객체가 된다.
public class OrdererSpec implements Specification<Order> {
private String ordererId;
public boolean isSatisfiedBy(Order agg) {
return agg.getOrdererId().getMemberId().getId().equals(ordererId);
}
}
클라이언트는 리포지터리에 전달해 주기만 하면 된다.
Specification<Order> ordererSpec = new OrdererSpec("madvirus");
List<Order> orders = orderRepository.findAll(ordererSpec);
스프링 데이터 JPA를 이용한 스펙 구현
public interface Specification<T> extends Serializable {
@Nullable
Predicate toPredicate(Root<T> root, CriteriaQuery query, CriteriaBuilder cb);
}
리포지터리/DAO에서 스펙 사용하기
public interface OrderSummaryDao
extends Repository<OrderSummary, String>{
List<OrderSummary> findAll(Specification<OrderSummary> spec);
}
스펙 조합
public interface Specification<T> extends Serializable {
static <T> Specification<T> not(@Nullable Specification<T> spec) { ... }
static <T> Specification<T> where(@Nullable Specification<T> spec) { ... }
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);
}
정렬 지정하기
스프링 데이터 JPA 는 두가지 방법을 사용해서 정렬을 지정 할 수 있다.
- 메서드 이름에 OrderBy 를 사용해서 정렬 기준 지정
- Sort 를 인자로 전달
public interface OrderSummaryRepository extends JpaRepository<OrderSummary, Integer> {
// 메서드 이름
List<OrderSummary> findAllByOrderByNumberDesc(String ordererId);
// Sort
List<OrderSummary> findAllByOrdererId(String ordererId, Sort sort);
}
페이징 처리하기
Sort 타입과 마찬가지로 find 메서드에 Pageable 타입 파라미터를 사용하면 페이징을 자동으로 처리해 준다.
public interface MemberDataDao extends Repository<MemberData, String>{
List<MemberData> findByNameLike(String name, Pageable pageable);
}
Pageable 타입은 인터페이스로 실제 Pageable 타입 객체는 PageRequest 클래스를 이용해서 생성한다.
PageRequest pageReq = PageRequest.of(1, 10);
List<MemberData> user = memberDataDao.findByNameLike("사용자%", pageReq);
Page 타입을 사용하면 데이터 목록뿐만 아니라 조건에 해당하는 전체 개수도 구할 수 있다.
public interface OrderSummaryRepository extends JpaRepository<OrderSummary, Integer> {
List<OrderSummary> findByOrderByNumberDesc(String ordererId, Pagable pagable);
// 목록뿐 아니라 조건에 해당하는 전체 개수 및 페이징 처리에 필요한 데이터도 함께 제공
Page<OrderSummary> findByOrderByNumberDesc(String ordererId, Pagable pagable);
}
page가 제공하는 메서드의 일부
Pageable pageReq = PageRequest.of(2,3);
Page<MemberData> page = memberDataDao.findByBlocked(false, pageReq);
List<MemberData> content = page.getContent();//조회 결과 목록
long totalElements - page.getTotalElements(); //조건에 해당하는 전체 개수
int totalPages - page.getTotalPages();//전체 페이지 번호
int number = page.getNumber();//현재 페이지 번호
int numberOfElements = page.getNumberOfElements();//조회 결과 개수
int size = page.getSize(); //페이지 크기