본문 바로가기
Spring

[9] 상품 생성

by 민지기il 2025. 3. 18.

[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