이 내용은 스프링 부트 쇼핑몰 프로젝트 with JPA 책을 학습한 내용입니다.
- Querydsl을 사용하기 위해서 Qdomain 생성
- build.gradle - [other] - [compileQuerydsl]
- 상품 조회 조건
- 상품 등록일
- 상품 판매 상태
- 상품명 또는 상품 등록자 아이디
- 상품 조회 조건을 담을 ItemSearchDto 클래스 생성
| import kr.spring.item.constant.ItemSellStatus; |
| import lombok.Getter; |
| import lombok.Setter; |
| import lombok.ToString; |
| |
| @Getter |
| @Setter |
| @ToString |
| public class ItemSearchDto { |
| |
| private String searchDateType; |
| |
| private ItemSellStatus searchSellStatus; |
| |
| private String searchBy; |
| |
| private String searchQuery; |
| } |
- Querydsl과 Spring Data Jpa를 함께 사용하기 위해서는 사용자 정의 리포지토리가 필요함② 사용자 정의 인터페이스 구현
- ③ Spring Data Jpa 리포지토리에서 사용자 정의 인터페이스 상속
- ① 사용자 정의 인터페이스 작성
- 상품 조회 조건을 담고 있는 itemSearchDto 객체와 페이징 정보를 담고 있는 pageable 객체를 파라미터로 받고, Page<Item> 객체를 반환
| import kr.spring.item.dto.ItemMainDto; |
| import kr.spring.item.dto.ItemSearchDto; |
| import kr.spring.item.entity.Item; |
| import org.springframework.data.domain.Page; |
| import org.springframework.data.domain.Pageable; |
| |
| public interface ItemRepositoryCustom { |
| |
| |
| Page<Item> getAdminItemPage(ItemSearchDto itemSearchDto, Pageable pageable); |
| |
| } |
- 클래스명 끝에 "Impl" 를 붙여야 정상적으로 동작함
- 클래스 생성 및 사용자 정의 인터페이스 구현
| public class ItemRepositoryCustomImpl implements ItemRepositoryCustom { |
| |
| private JPAQueryFactory queryFactory; |
| |
| public ItemRepositoryCustomImpl(EntityManager em) { |
| queryFactory = new JPAQueryFactory(em); |
| } |
| } |
- BooleanExpression 을 통해 where 절에 적용될 조회 조건을 생성
- BooleanExpresiion 값이 null 이면 해당 조회 조건을 사용하지 않겠다는 의미 (=all)
- 상품 등록일 조건
| |
| private BooleanExpression regDtsAfter(String searchDateType) { |
| |
| LocalDateTime dateTime = LocalDateTime.now(); |
| |
| if(StringUtils.equals("all", searchDateType) || searchDateType == null) { |
| return null; |
| } else if(StringUtils.equals("1d", searchDateType)) { |
| dateTime = dateTime.minusDays(1); |
| } else if(StringUtils.equals("1w", searchDateType)) { |
| dateTime = dateTime.minusWeeks(1); |
| } else if(StringUtils.equals("1m", searchDateType)) { |
| dateTime = dateTime.minusMonths(1); |
| } else if(StringUtils.equals("6m", searchDateType)) { |
| dateTime = dateTime.minusMonths(6); |
| } |
| |
| return item.regTime.after(dateTime); |
| } |
| private BooleanExpression searchSellStatusEq(ItemSellStatus searchSellStatus) { |
| |
| return searchSellStatus == null ? null : item.itemSellStatus.eq(searchSellStatus); |
| } |
| private BooleanExpression searchByLike(String searchBy, String searchQuery) { |
| |
| |
| |
| if(StringUtils.equals("itemNm", searchBy)) { |
| return item.itemNm.like("%" + searchQuery + "%"); |
| } |
| |
| else if (StringUtils.equals("createdBy", searchBy)) { |
| return item.createdBy.like("%" + searchQuery + "%"); |
| } |
| |
| return null; |
| } |
- QueryFactory 를 이용하여 Querydsl 쿼리문 생성offset : 데이터를 가지고 올 시작 인덱스를 지정limit : 한 번에 가지고 올 최대 개수를 지정
- fetchResult() 메소드를 이용하여 조회 대상 리스트 및 전체 개수를 포함하는 QueryResults 객체 반환
- Page 클래스의 구현체인 PageImpl 객체로 반환
| @Override |
| public Page<Item> getAdminItemPage(ItemSearchDto itemSearchDto, Pageable pageable) { |
| |
| List<Item> content = queryFactory |
| .selectFrom(item) |
| .where(regDtsAfter(itemSearchDto.getSearchDateType()), |
| searchSellStatusEq(itemSearchDto.getSearchSellStatus()), |
| // 어떤 방법으로 어떤 디비를 던질 것인지 |
| searchByLike(itemSearchDto.getSearchBy(), itemSearchDto.getSearchQuery())) |
| .orderBy(item.id.desc()) |
| .offset(pageable.getOffset()) // 시작할 위치 |
| .limit(pageable.getPageSize()) // 가져올 갯수 |
| .fetch(); // 리스트를 가져옴 |
| |
| long total = queryFactory.select(Wildcard.count).from(item) |
| .where(regDtsAfter(itemSearchDto.getSearchDateType()), |
| searchSellStatusEq(itemSearchDto.getSearchSellStatus()), |
| searchByLike(itemSearchDto.getSearchBy(), itemSearchDto.getSearchQuery())) |
| .fetchOne(); |
| |
| return new PageImpl<>(content, pageable, total); |
| } |
- JpaRepository 를 구현한 ItemRepository 에서 ItemRepositoryCustom을 상속
| public interface ItemRepository extends JpaRepository<Item, Long>, |
| QuerydslPredicateExecutor<Item>, ItemRepositoryCustom { |
- 위에서 만든 사용자 정의 조회문(=메소드)을 수행하는 로직 추가
- 조회 기능이므로 읽기 전용 상태로 지정
| |
| public Page<Item> getAdminItemPage(ItemSearchDto itemSearchDto, Pageable pageable) { |
| |
| return itemRepository.getAdminItemPage(itemSearchDto, pageable); |
| |
| } |
- Mapping 파라미터로 객체를 지정하면(ItemSearchDto) 자동으로 new 객체 생성
- URL을 통해 페이지 번호가 넘어오는 경우 @PathVariable로 변수 값 매핑
| |
| @GetMapping({"/admin/items", "/admin/items/{page}"}) |
| public String itemList(ItemSearchDto itemSearchDto, Model model, |
| @PathVariable("page")Optional<Integer> page) { |
| |
| |
| Pageable pageable = PageRequest.of(page.isPresent() ? page.get() : 0, 10); |
| Page<Item> items = itemService.getAdminItemPage(itemSearchDto, pageable); |
| |
| model.addAttribute("items", items); |
| |
| model.addAttribute("itemSearchDto", itemSearchDto); |
| |
| model.addAttribute("maxPage", 5); |
| |
| return "item/itemList"; |
| } |