상품관리는 QueryDsl에 조건문을 줘서 원하는 값을 조회하는 방법을 쓰도록 하겠습니다. 조건은 상품 등록일, 상품 판매 상태, 상품명, 상품 등록자, 가격을 추가로 작성할 수 있습니다.
기본적으로 상품관리 페이지는 등록한 모든 상품을 보여주며 조건을 통해서 필터를 걸어서 조회하도록 하겠습니다.
public class ItemSearchDto {
// all : 상품 등록 전체
// 1d : 최근 하루 동안 등록된 상품
// 1w : 최근 일주일 동안 등록된 상품
// 1m : 최근 한 달 동안 등록된 상품
// 6m : 최근 6개월 동안 등록된 상품
private String searchDateType;
private ItemSellStatus searchSellStatus;
// itemNm : 상품명
// createBy : 상품 등록자 ID
private String searchBy;
private String searchQuery = "";
private Long price;
}
Spring Data JPA와 같이 연동시키기 위해 Custom 인터페이스와 구현체를 만들어 보겠습니다. 꼭 구현체는 Impl을 붙여줘야 합니다. 자세한 내용은 아래의 링크에 확인 가능합니다.
https://docs.spring.io/spring-data/jpa/docs/2.1.3.RELEASE/reference/html/#repositories.custom-implementations
public interface ItemRepositoryCustom {
Page<Item> getAdminItemPage(ItemSearchDto itemSearchDto, Pageable pageable);
}
Querydsl에서는 BooleanExpression을 사용해서 간단하면서도 재사용성 가능하도록 동적쿼리를 만들 수 있게 해줍니다.
BooleanExpression의 값이 null이라면 조건문에 무시되기 때문에 해당 조건에 만족하지 않으면 null로 반환시키도록 합니다.
public class ItemRepositoryCustomImpl implements ItemRepositoryCustom{
private JPAQueryFactory queryFactory;
public ItemRepositoryCustomImpl(EntityManager em){
this.queryFactory = new JPAQueryFactory(em);
}
private BooleanExpression searchSellStatusEq(ItemSellStatus searchSellStatus){
return searchSellStatus == null ? null : QItem.item.itemSellStatus.eq(searchSellStatus);
}
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 QItem.item.regTime.after(dateTime);
}
private BooleanExpression searchByLike(String searchBy, String searchQuery){
if(StringUtils.equals("itemNm", searchBy)){
return QItem.item.itemNm.like("%" + searchQuery + "%");
} else if(StringUtils.equals("createdBy", searchBy)){
return QItem.item.createdBy.like("%" + searchQuery + "%");
}
return null;
}
private BooleanExpression underPrice(Long price){
if( price <= 0){
return null;
}
return QItem.item.price.loe(price);
}
private BooleanExpression itemNmLike(String searchQuery){
return StringUtils.isEmpty(searchQuery) ? null : QItem.item.itemNm.like("%" + searchQuery + "%");
}
@Override
public Page<Item> getAdminItemPage(ItemSearchDto itemSearchDto, Pageable pageable) {
List<Item> results = queryFactory
.selectFrom(QItem.item)
.where(regDtsAfter(itemSearchDto.getSearchDateType()),
searchSellStatusEq(itemSearchDto.getSearchSellStatus()),
searchByLike(itemSearchDto.getSearchBy(),
itemSearchDto.getSearchQuery()),
underPrice(itemSearchDto.getPrice())
.orderBy(QItem.item.id.desc())
.offset(pageable.getOffset())
.limit(pageable.getPageSize())
.fetch();
long total = results.size();
return new PageImpl<>(results, pageable, total);
}
만들어 놓은 ItemRepositoryCustom을 상속받게 합니다.
public interface ItemRepository extends JpaRepository<Item, Long>, QuerydslPredicateExecutor<Item>, ItemRepositoryCustom {
상품관리 페이지("/admin/items")에서 페이지를 누르면 "/admin/items/{page}"로 전달되어 페이징 처리된 값을 받게 됩니다.
먼저 page 값이 안들어 있을 수 있기 때문에 Optional로 감싸준 뒤, 유효성 검사 후 PageRequest.of 를 사용하여 Pageable을 생성합니다. (PageRequest는 Pageable의 구현체)
한 페이지에 5개로 제한했습니다.
@GetMapping(value = {"/admin/items", "/admin/items/{page}"})
public String itemManage(ItemSearchDto itemSearchDto, @PathVariable("page")Optional<Integer> page, Model model){
Pageable pageable = PageRequest.of(page.isPresent() ? page.get() : 0, 5);
Page<Item> items = itemService.getAdminItemPage(itemSearchDto, pageable);
model.addAttribute("items", items);
model.addAttribute("itemSearchDto", itemSearchDto);
model.addAttribute("maxPage", 5);
return "item/itemMng";
}
성능 최적화를 위하여 Transactional을 readOnly로 설정합니다. Service는 전달받은 메시지를 그대로 Repository로 전달해줍니다.
@Transactional(readOnly = true)
public Page<Item> getAdminItemPage(ItemSearchDto itemSearchDto, Pageable pageable){
return itemRepository.getAdminItemPage(itemSearchDto, pageable);
}