기록이 힘이다.

[도메인 주도 개발 시작하기] 5. 스프링 데이터 JPA를 이용한 조회 기능 본문

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(); //페이지 크기