상품 조회 조건을 설정 후 페이징 기능을 통해 일정 개수만 볼 수 있도록 구현
조회 조건
- 상품 등록일
- 상품 판매 기간
- 상품명 또는 상품 등록자 아이디
Querydsl을 이용해 QDomain 클래스를 생성한다.
@Getter
@Setter
public class ItemSearchDto {
/*
현재 시간과 상품 등록일을 비교해서 조회
- all : 상품 등록일 전체
- 1d : 최근 하루 동안 등록된 상품
- 1w : 최근 일주일 동안 등록된 상품
- 1m : 최근 한달 동안 등록된 상품
- 6m : 최근 6개월 동안 등록된 상품
*/
private String searchDateType;
// 상품의 판매상태를 기준으로 조회
private ItemSellStatus searchSellStatus;
/* 아딴 유형으로 조회할지 선택
- itemNm
- createdBy
*/
private String searchBy;
// 조회할 검색어 저장할 변수
private String searchQuery = "";
}
사용자 정의 레포지터리를 정의한다.
- 사용자 정의 인터페이스 작성
- 사용자 정의 인터페이스 구현
- Spring Data Jpa의 Repository에서 사용자 정의 interface 상속
public interface ItemRepositoryCustom {
// 상품 조회 조건을 담는 itemSearchDto, 페이징 정보를 담는 Pageable
// Page<Item> 객체 반환
Page<Item> getAdminItemPage(ItemSearchDto itemSearchDto, Pageable pageable);
}
Querydsl에서 BooleanExpression
(where절에서 사용할 수 있는 값)을 지원
public class ItemRepositoryCustomImpl implements ItemRepositoryCustom {
// 2. 동적 쿼리를 생성하기 위해 JPAQueryFactory 사용
private JPAQueryFactory queryFactory;
// 3.JpaQueryFactory 생성자에 EntityManager 넣음
public ItemRepositoryCustomImpl(EntityManager em){
this.queryFactory = new JPAQueryFactory(em);
}
// 4. 상품 판매 상태가 전체(null)이면 null 반환 / 아니면 해당 조건의 상품만 조회
private BooleanExpression searchSellStatusEq(ItemSellStatus searchSellStatus){
return searchSellStatus == null ? null : QItem.item.itemSellStatus.eq(searchSellStatus);
}
// 5. searchDateType을 1m으로 하면, dateTime이 한달 전으로 설정 되고, 최근 한 달 동안 등록된 상품만 등록하도록 설정
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);
}
// 6. searchBy 값이 상품명에 검색어를 포함 혹은 생성자의 아이디에 검색어를 포함하는지 구분 후 조회
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;
}
@Override
public Page<Item> getAdminItemPage(ItemSearchDto itemSearchDto, Pageable pageable) {
// 7. queryFactory로 쿼리를 생성
QueryResults<Item> results = queryFactory.selectFrom(QItem.item) // selectForm() : 상품 데이터를 조회하기 위해 QItem의 item을 지정
.where(regDtsAfter(itemSearchDto.getSearchDateType()), // where : BooleanExpression 반환하는 조건문들 넣어줌
searchSellStatusEq(itemSearchDto.getSearchSellStatus()),
searchByLike(itemSearchDto.getSearchBy(),
itemSearchDto.getSearchQuery()))
.orderBy(QItem.item.id.desc()) // 내림차순
.offset(pageable.getOffset()) // offset : 데이터를 가지고 올 시작 인덱스 지정
.limit(pageable.getPageSize()) // limit : 한번에 가져올 최대 개수 지정
.fetchResults(); // fetchResults : 조회한 리스트 및 전체 개수를 포함하는 QueryResults를 반환 (상품 데이터 리스트 조회, 상품 데이터 전체 개수 조회 실행)
List<Item> content = results.getResults();
long total = results.getTotal();
// 8. 조회 후 데이터를 PageImpl 객체로 반환
return new PageImpl<>(content, pageable, total);
}
// 검색어가 null 아니면 상품명에 해당 검색어가 포함되는 상품을 조회
private BooleanExpression itemNmLike(String searchQuery){
return StringUtils.isEmpty(searchQuery) ? null : QItem.item.itemNm.like("%"+searchQuery + "%");
}
@Override
public Page<MainItemDto> getMainItemPage(ItemSearchDto itemSearchDto, Pageable pageable) {
QItem item = QItem.item;
QItemImg itemImg = QItemImg.itemImg;
QueryResults<MainItemDto> results = queryFactory
.select(
new QMainItemDto( // QMainItemDto 생성자에 반환할 값들 입력
item.id,
item.itemNm,
item.itemDetail,
itemImg.imgUrl,
item.price
)
)
.from(itemImg)
.join(itemImg.item, item) // itemImg와 item을 내부 조인
.where(itemImg.repImgYn.eq("Y")) // 상품 이미지는 대표 상품의 이미지만 가져옴
.where(itemNmLike(itemSearchDto.getSearchQuery()))
.orderBy(item.id.desc()) // 내림차순
.offset(pageable.getOffset())
.limit(pageable.getPageSize())
.fetchResults();
List<MainItemDto> content = results.getResults();
long total = results.getTotal();
return new PageImpl<>(content, pageable, total);
}
}
마지막으로 ItemRepository 인터페이스에서 ItemRepositoryCustom 상속
getAdminItemPage()를 ItemService에서 사용해보자
@Transactional(readOnly = true)
public Page<Item> getAdminItemPage(ItemSearchDto itemSearchDto, Pageable pageable){
return itemRepository.getAdminItemPage(itemSearchDto, pageable);
}
@QueryProjection으로 DTO 객체로 결과를 받는 방법을 사용
위 어노테이션으로 Item 객체로 받은 값을 DTO 객체로 바로 뽑아 사용
@Getter @Setter
public class MainItemDto {
private Long id;
private String itemNm;
private String itemDetail;
private String imgUrl;
private Integer price;
@QueryProjection // Querydsl로 조회 시 MainItemDto로 바로 받음
public MainItemDto(Long id, String itemNm, String itemDetail, String imgUrl,Integer price){
this.id = id;
this.itemNm = itemNm;
this.itemDetail = itemDetail;
this.imgUrl = imgUrl;
this.price = price;
}
}
해당 Dto에 대한 QDto 파일을 생성한다.
Page<MainItemDto> getMainItemPage(ItemSearchDto itemSearchDto, Pageable pageable);
위 커스텀 레포지토리를 구현하는 구현체를 작성한다.
private BooleanExpression itemNmLike(String searchQuery){
return StringUtils.isEmpty(searchQuery) ? null : QItem.item.itemNm.like("%" + searchQuery + "%");
}
@Override
public Page<MainItemDto> getMainItemPage(ItemSearchDto itemSearchDto, Pageable pageable) {
QItem item = QItem.item;
QItemImg itemImg = QItemImg.itemImg;
QueryResults<MainItemDto> results = queryFactory
.select(
new QMainItemDto(
item.id,
item.itemNm,
item.itemDetail,
itemImg.imgUrl,
item.price)
)
.from(itemImg)
.join(itemImg.item, item)
.where(itemImg.repimgYn.eq("Y"))
.where(itemNmLike(itemSearchDto.getSearchQuery()))
.orderBy(item.id.desc())
.offset(pageable.getOffset())
.limit(pageable.getPageSize())
.fetch();
List<MainItemDto> content = results.getResults();
long total = results.getTotal();
return new PageImpl<>(content, pageable, total);
}
메인 페이지의 상품 클릭 시 상셍 정보를 보여주는 페이지를 구현해보자