rudu_std
JPA 쿼리 본문
@Query("SELECT c FROM CartItem c JOIN FETCH c.product " +
"JOIN FETCH c.product.images " +
"WHERE c.cart.customer = :customer ORDER BY c.itemNo DESC")
이 @Query는 CartItem 엔티티를 조회하는데, CartItem과 관련된 Product, 그리고 Product와 연결된 ProductImage들을 즉시 로딩(FETCH JOIN)하여 한 번에 가져옵니다. 쿼리에서 사용된 각 부분을 분석하면 다음과 같다:
- SELECT c FROM CartItem c : CartItem 엔티티(테이블 tbl_cartItem)를 선택하여 c라는 별칭을 사용하고 있다.
- JOIN FETCH c.product : CartItem과 Product가 ManyToOne 관계로 연결되어 있으므로, CartItem이 참조하는 Product 정보를 즉시 로딩하여 가져온다. 여기서 FETCH는 기본 Lazy 로딩을 무시하고, 즉시 조인을 통해 Product 데이터를 함께 가져오는 역할을 한다.
- JOIN FETCH c.product.images : Product 엔티티와 ProductImage가 연관되어 있으므로, Product에 있는 이미지 목록(SortedSet<ProductImage>)을 즉시 로딩하여 가져온다.
- WHERE c.cart.customer = :customer : CartItem이 참조하는 Cart의 customer 컬럼과 :customer(쿼리 파라미터 값)를 비교하여 조건에 맞는 CartItem을 필터링한다.
- ORDER BY c.itemNo DESC : 결과를 itemNo 기준으로 내림차순으로 정렬한다.
즉, 이 쿼리는 특정 customer가 소유한 장바구니(Cart)에 담긴 모든 상품(Product) 정보를 가져오며, 그 상품에 해당하는 이미지들도 즉시 불러온다.
엔티티 클래스별 매핑 분석
- CartItem 클래스 (tbl_cartItem 테이블과 매핑)
- CartItem은 ManyToOne 관계로 Product와 Cart와 연결되어 있다. 즉, 여러 CartItem이 하나의 Product와 하나의 Cart에 속할 수 있다.
- fetch = FetchType.LAZY로 선언되어 있어 기본적으로는 지연 로딩 방식이다. 하지만 쿼리에서 JOIN FETCH를 사용해 즉시 로딩이 발생한다.
-
java코드 복사@ManyToOne(fetch = FetchType.LAZY) private Product product; @ManyToOne(fetch = FetchType.LAZY) private Cart cart;
- Cart 클래스 (tbl_cart 테이블과 매핑)
- Cart는 customer라는 필드를 가지고 있으며, 이 필드는 쿼리의 WHERE 절에서 사용된다.
-
java코드 복사private String customer;
- Product 클래스 (tbl_product 테이블과 매핑)
- Product는 SortedSet<ProductImage>를 사용하여 이미지들을 저장한다. 여기서 ElementCollection을 통해 tbl_product_image 테이블과 매핑되며, 각 상품 번호(pno)에 해당하는 이미지를 관리한다.
- fetch = FetchType.LAZY로 선언되었지만, 쿼리에서 JOIN FETCH를 통해 즉시 로딩을 적용하고 있다.
-
java코드 복사@ElementCollection(fetch = FetchType.LAZY) @CollectionTable(name = "tbl_product_image", joinColumns = @JoinColumn(name = "pno")) private SortedSet<ProductImage> images = new TreeSet<>();
3. JPA 연관관계와 매핑 특징
JPA가 각 엔티티 클래스와 데이터베이스 테이블의 관계를 처리하는 방식에 대해 설명하면 다음과 같다:
- CartItem과 Product는 @ManyToOne 관계:
- 여러 CartItem이 하나의 Product를 참조한다.
- 이 관계 덕분에 쿼리에서 JOIN FETCH c.product를 사용하면, JPA는 CartItem과 관련된 Product를 자동으로 가져온다.
- Product와 ProductImage는 @ElementCollection 관계:
- 하나의 Product가 여러 ProductImage를 가질 수 있다.
- 쿼리에서 JOIN FETCH c.product.images를 사용하면, JPA는 Product와 연관된 이미지들을 자동으로 로딩한다.
- JPA가 자동으로 처리하는 매핑:
- @Query에 명시된 쿼리는 CartItem, Product, ProductImage, Cart 등 여러 테이블 간의 조인을 필요로 하지만, JPA는 객체 관계에 기반하여 필요한 SQL 조인을 내부적으로 생성하고 처리한다.
- 즉, 개발자가 SQL에서 모든 조인 조건을 명시할 필요 없이, JPA가 @ManyToOne, @ElementCollection과 같은 매핑 정보를 이용해 연관된 데이터를 자동으로 로딩하고 처리할 수 있다.
4. 왜 간결한가?
쿼리가 간결할 수 있는 이유는 JPA가 객체 간의 관계를 자동으로 인식하고 이를 기반으로 SQL 쿼리를 생성하기 때문이다.
- 예를 들어, CartItem 클래스에서 product 필드가 Product 클래스와 @ManyToOne 관계로 매핑되어 있으므로, JPA는 이를 이용해 tbl_cartItem과 tbl_product 테이블을 자동으로 조인 한 다.
- 마찬가지로 Product 클래스에 있는 images 필드가 @ElementCollection으로 선언되어 있으므로, JPA는 tbl_product_image 테이블을 자동으로 조인하고 필요한 이미지를 가져온다.
이처럼 JPA는 엔티티 클래스의 연관관계에 따라 SQL을 자동으로 생성해주기 때문에, 개발자가 직접 복잡한 SQL을 작성할 필요 없이, 간단한 @Query만으로도 복잡한 데이터를 가져올 수 있다.
5. 변환된 SQL 문
@Query("SELECT c FROM CartItem c JOIN FETCH c.product " +
"JOIN FETCH c.product.images " +
"WHERE c.cart.customer = :customer ORDER BY c.itemNo DESC")
SELECT ci.*, p.*, pi.*
FROM tbl_cart_item ci
JOIN tbl_product p ON ci.product_pno = p.pno
LEFT JOIN tbl_product_image pi ON p.pno = pi.pno
WHERE ci.cart_cno IN (
SELECT c.cno
FROM tbl_cart c
WHERE c.customer = 'user99'
)
ORDER BY ci.item_no DESC;
'Java' 카테고리의 다른 글
Optional (0) | 2024.09.04 |
---|---|
stream && Ramda (0) | 2024.09.03 |
JDBC 정리 (0) | 2024.08.02 |
JDBC (0) | 2024.08.01 |
람다 표현식 (Lambda Expression) (0) | 2024.07.24 |