[1] 엔티티 생성 -- product
@Entity
@Getter
@Table(name = "tbl_product")
@Builder
@AllArgsConstructor
@NoArgsConstructor
@ToString(exclude = "imageList")
public class Product {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long pno;
private String pname;
private int price;
private String pdesc;
private boolean delFlag;
@ElementCollection // 별도의 table 생성
@Builder.Default
private List<ProductImage> imageList = new ArrayList<>();
public void changePrice (int price){
this.price = price;
}
public void changeDesc (String desc){
this.pdesc = pdesc;
}
public void changeName (String name){
this.pname = pname;
}
public void addImage(ProductImage image){
image.setOrd(imageList.size());
imageList.add(image);
}
public void addImageString(String fileName){
ProductImage productImage = ProductImage.builder()
.fileName(fileName)
.build();
addImage(productImage);
}
public void clearList(){
this.imageList.clear();
}
}
현재 @ToString(exclude = "imageList") 이렇게 imageList를 제외하고 있다.
product와 productimage를 db에서 2번 가져와야 하는 lazy 로딩이기 때문에 이렇게 제외시킨 것
[2] 엔티티 생성 -- product image
@Embeddable
@Getter
@ToString
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class ProductImage {
public String fileName;
private int ord;
public void setOrd(int ord){
this.ord = ord;
}
}
[3] product repository + test 진행
public interface ProductRepository extends JpaRepository<Product, Long> {
}
@SpringBootTest
@Log4j2
public class ProductRepositoryTests {
@Autowired
private ProductRepository productRepository;
@Test
public void testInsert(){
Product product = Product.builder().pname("Test ").pdesc("Test Desc").price(1000).build();
product.addImageString(UUID.randomUUID()+"_"+"IMAGE1.jpg");
product.addImageString(UUID.randomUUID()+"_"+"IMAGE2.jpg");
productRepository.save(product);
}
}
[3] @EntityGraph
public interface ProductRepository extends JpaRepository<Product, Long> {
@EntityGraph(attributePaths = "imageList")
@Query("select p from Product p where p.pno = :pno")
Optional<Product> selectOne( @Param("pno") Long pno);
}
Product 엔티티를 보면 2번 쿼리가 날라간다.
@EntityGraph는 N+1 문제를 해결하고, 연관된 엔티티를 한 번의 쿼리로 가져오기 위해 사용된다.
attributePaths를 사용하면 fetch join과 같은 효과를 내면서 연관된 엔티티를 함께 로드할 수 있다.
>Querydsl에서 @ElemenetCollection 처리하기
[4] page 처리하기
public interface ProductRepository extends JpaRepository<Product, Long> {
@EntityGraph(attributePaths = "imageList")
@Query("select p from Product p where p.pno = :pno")
Optional<Product> selectOne( @Param("pno") Long pno);
@Modifying
@Query("update Product p set p.delFlag = :delFlag where p.pno = :pno")
void updateToDelete(@Param("pno") Long pno, @Param("delFlag") boolean flag);
@Query("select p.pi from Product p left join p.imageList pi where pi.ord = 0 and p.delFlag = false")
Page<Object[]> selectList(Pageable pageable);
}
[5] Page test 실행
@Test
public void testList(){
Pageable pageable = PageRequest.of(0,10, Sort.by("pno").descending());
Page<Object[]> result = productRepository.selectList(pageable);
result.getContent().forEach(arr -> log.info(Arrays.toString(arr)));
}
[6] ProductSearch 인터페이스
public interface ProductSearch {
PageResponseDTO<ProductDTO> searchList (PageRequestDTO pageRequestDTO);
}
[7] ProductSearchImpl
@Log4j2
public class ProductSearchImpl extends QuerydslRepositorySupport implements ProductSearch {
public ProductSearchImpl(){
super(Product.class);
}
@Override
public PageResponseDTO<ProductDTO> searchList(PageRequestDTO pageRequestDTO) {
log.info("------searchList------");
Pageable pageable = PageRequest.of(
pageRequestDTO.getPage()-1,
pageRequestDTO.getSize(),
Sort.by("pno").descending());
QProduct product = QProduct.product;
QProductImage productImage = QProductImage.productImage;
JPQLQuery<Product> query = from(product);
query.leftJoin(product.imageList, productImage);
query.where(productImage.ord.eq(0));
getQuerydsl().applyPagination(pageable, query);
List<Product> productList = query.fetch();
long count = query.fetchCount();
return null;
}
}
> 역할
DB에서 상품을 검색하고, 페이지 단위로 가져오는 기능을 함.
QueryDSL을 사용해서 상품과 상품 이미지 데이터를 조인(join) 해서 가져옴.
가져온 데이터를 페이지네이션(한 번에 10개씩, 20개씩 나눠서 보여줌) 해서 반환함
> 코드
(1)
public ProductSearchImpl(){
super(Product.class);
}
: QuerydslRepositorySupport에 "나는 Product라는 테이블을 다룰 거야!" 라고 알려줌.
(2)
@Override
public PageResponseDTO<ProductDTO> searchList(PageRequestDTO pageRequestDTO) {
사용자가 페이지별로 상품을 보고 싶을 때 요청을 받아서 상품을 데이터베이스에서 검색한 후 반환하는 역할을 함
(3)
Pageable pageable = PageRequest.of(
pageRequestDTO.getPage()-1,
pageRequestDTO.getSize(),
Sort.by("pno").descending());
사용자가 10개씩 보고 싶다, 페이지 2번을 보고 싶다라는 요청을 받아서 적절한 페이지네이션 설정을 해줌
Sort.by("pno").descending() → 상품 번호(pno)를 기준으로 최신 순으로 정렬(내림차순)한다.
(4)
QProduct product = QProduct.product;
QProductImage productImage = QProductImage.productImage;
Product 테이블, ProductImage (상품 이미지) 테이블을 가져올 거임
(5)
JPQLQuery<Product> query = from(product); SELECT * FROM Product 역할
query.where(productImage.ord.eq(0)); 첫 번째 이미지(대표 이미지)만 가져오기
getQuerydsl().applyPagination(pageable, query); 한 페이지에 몇 개씩 보여줄지 설정
List<Product> productList = query.fetch(); 데이터베이스에서 상품 목록을 실제로 가져오는 부분
'Spring' 카테고리의 다른 글
[1] 자바 중급 문법 1 (0) | 2025.03.19 |
---|---|
[8] 소셜 로그인 구현 2 (0) | 2025.03.18 |
[7] 소셜 로그인 구현 (0) | 2025.03.12 |
[6] 이미지 파일 업로드 (0) | 2025.03.07 |
[6] Todo get, register, modify, remove (0) | 2025.03.07 |