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(); // 페이지 크기
프로퍼티를 비교하는 findBy프로퍼티 형식의 메서드는 Pageable 타입을 사용하더라도 리턴 타입이 List면 COUNT 쿼리를 실행하지 않는다.
스펙을 사용하는 메서드에 Pageable 타입을 함께 쓰면 리턴 타입이 Page가 아니어도 COUNT 쿼리를 실행한다. 스펙을 사용하고 페이징 처리를 하면서 COUNT 쿼리는 실행하고 싶지 않다면 스프링 데이터 JPA가 제공하는 커스텀 리포지터리 기능을 이용해서 직접 구현해야 한다.
스펙 빌더
조건에 따라 특정 스펙을 조합할 지 말 지 다루기 위해 스펙 빌더 만들어 사용하면 코드가 간결해진다.
Specification<MemberData> spec =SpecBuilder.builder(MemberData.class).ifTrue(searchRequest.isOnlyNotBlocked(), () ->MemberDataSpecs.nonBlocked()).ifHasText(searchRequest.getName(), name ->MemberDataSpecs.nameLike(searchRequest.getName())).toSpec();List<MemberData> result =memberDataDao.findAll(spec,PageRequest.of(0,5));
JPA는 쿼리 결과에서 임의의 객체를 동적으로 생성하는 기능을 제공한다. 아래와 같이 select 절에서 조회 결과를 사용해 바로 새로운 객체를 생성할 수 있다.
publicinterfaceOrderSummaryRepositoryextendsRepository<OrderSummary,String> {@Query(""" select new com.myshop.order.query.dto.OrderView( o.number, o.state, m.name, m.id, p.name ) from Order o join o.orderLines ol, Member m, Product p where o.orderer.memberId.id = :ordererId and o.orderer.memberId.id = m.id and index(ol) = 0 and ol.productId.id = p.id order by o.number.number desc """)List<OrderView> findOrderView(String ordererId);}
OrderView와 같이 조회 전용 모델을 사용하여 표현 영역에서 사용자에게 꼭 필요한 데이터만을 보여주도록 할 수 있다.
@Subselect
쿼리 결과를 @Entity로 매핑할 수 있는 기능이다.
조회 쿼리를 어노테이션의 값으로 입력하여, 쿼리 실행 결과를 매핑할 테이블처럼 사용한다.
@Subselect의 값으로 지정한 쿼리는 from 절의 서브 쿼리로 사용한다. 즉 findById 등의 메서드를 사용해 조회하고자 할 때 from 절 내부에 @Subselect에 정의한 서브 쿼리가 들어가게 되는 것이다.
@Entity@Immutable@Subselect(""" select o.order_number as number, o.version, o.orderer_id, o.orderer_name, o.total_amounts, o.receiver_name, o.state, o.order_date, p.product_id, p.name as product_name from purchase_order o inner join order_line ol on o.order_number = ol.order_number cross join product p where ol.line_idx = 0 and ol.product_id = p.product_id""")@Synchronize({"purchase_order","order_line","product"})publicclassOrderSummary { @IdprivateString number; @Column(name ="orderer_id")privateString ordererId;privateString productId;// ...}
@Subselect를 이용해 조회한 엔티티의 필드를 수정하면 하이버네이트는 변경 내역을 반영하는 update 쿼리를 실행하지만 매핑 한 테이블이 없으므로 에러가 발생한다. 이러한 문제 발생을 방지하기 위해 @Immutable을 사용하여 해당 엔티티의 매핑 필드/프로퍼티가 변경되더라도 DB에 반영하지 않고 무시하도록 한다.
@Synchronize를 이용해 엔티티와 관련된 테이블에 변경이 발생하도록 하는 메서드가 호출되었을 경우 이를 먼저 플러시하고 조회하도록 한다.