기존 양방향
JPA 연관관계를 사용하여 사용자가 거래내역을 조회했을 때, 거래했던 상품들을 보여주고자 했다.
Rental 클래스에 주문내역을 보여주고, RentalProducts에서 한 주문내역에 대한 제품들을 보여주도록 관계를 만들었다.
💡 양방향 매핑 규칙
- 객체의 두 관계중 하나를 연관관계의 주인으로 지정
- 연관관계의 주인만이 외래 키를 관리(등록, 수정)
- 주인이 아닌쪽은 읽기만 가능
- 주인은 mappedBy 속성 사용X
- 주인이 아니면 mappedBy 속성으로 주인 지정
💡 연관 관계의 주인(Owner) 지정 방법
외래키가 있는 곳을 주인으로 지정할것!→ Rental의 객체에서 값을 바꿨는데, RENTALPRODUCTS테이블에 쿼리문이 나가면 혼란스럽다.
@OneToMany, @ManyToOne을 사용하여 양방향 매핑을 하고자 했으나, 아래 사진 처럼 Rental클래스를 참조한 RentalProducts에서 rental_id가 조회되지 않았다.
알고보니.. 양방향 매핑시 가장 많이 하는 실수 중 연관관계의 주인 값에 입력하지 않는 경우에 값이 들어가지 않는다고 한다.
- 항상 양쪽에 값을 설정해야 한다.
// DTO 객체를 RentalEntity로 매핑
ModelMapper mapper = new ModelMapper();
mapper.getConfiguration().setMatchingStrategy(MatchingStrategies.STRICT);
RentalEntity rentalEntity = mapper.map(addRentedDeliveryOrderDto, RentalEntity.class);
// 매핑된 RentalEntity 객체에 저장된 rentalId 및 OrderedAt 주문일 설정
rentalEntity.setRentalId(saveRental.getRentalId());
rentalEntity.setOrderedAt(LocalDateTime.now());
// RentalProductsEntity 목록 생성 및 각 제품 저장
List<RentalProductsEntity> productList = new ArrayList<>();
for(RentalProductsEntity rentalProducts : rentalEntity.getRentalProducts()) {
// rentalProducts 객체의 rental 필드를 설정
// 💡 RentalEntity 간의 연관 관계를 설정하기 위해서입니다.
// 💡 단순히 productList에 추가하는 것만으로는 rentalProducts 객체의 rental 필드가 자동으로 설정되지 않습니다.
// 💡 따라서 rentalProducts 객체에 rentalEntity를 명시적으로 설정해야 합니다.
rentalProducts.setRental(rentalEntity);
// 리스트에 rentalProducts 객체를 추가
productList.add(rentalProducts);
}
- 양방향 매핑 시 무한 루프를 조심해야 한다.
→ toString(), lombok , JSON 생성 라이브러리 사용시 양방향으로 매핑되면, 무한루프에 빠진다
- toString 사용 x, lombok에서 toString 사용하는것 생성 x, JSON에서 엔티티 반환하는것 사용하지 말것 → dto로 변환해서 반환
양방향 매핑 정리
- 단방향 매핑으로도 이미 연관관계 매핑은 완료
- 양방향 매핑은 반대 방향으로 조회 기능이 추가된 것
- JPQL에서 역방향으로 탐색할 일이 많다.
- 단방향 매핑을 우선하고, 양방향이 필요할 때 추가 해도 된다. (양방향 추가시 테이블을 바꾸지 않기 때문에 코드 몇줄만 추가하면 되기 때문)
- 연관관계의 주인을 정하는 기준→ 외래 키의 위치를 기준으로 정할 것!
의문점
여러 글을 JPA 양방향 관계를 찾아보면서 알게 되면서 무조건 양방향이 좋은 것이 아니라는 것이었다.
그렇다면 지금 Rental과 RentalProducts는 양방향 관계가 필요한게 맞을까라는 의문을 가지게 되었다.
1.
Rental → RentalProducts 단방향 참조가 필요한가?
Rental.getRentalProducts가 필요한가?
1번 거래내역에 1번 거래내역에 대한 제품을 보여주어야 한다.(필요하다 ⭕)
양방향이 아니라 여기에만 있으면 되는게 아닌가??
2.
RentalProducts → Rental 단방향 참조가 필요한가?
RentalProducts.getRental이 필요한가?
RentalProducts제품에서 1번 거래내역이라는 것을 확인하기 위해 RentalId가 필요하지 않다. (필요하다 ❌)
단방향으로 변경
단방향 1:N만으로도 충분하다는 것을 알게 되었다.
단방향 1:N vs. @Embeddable
- 그렇다면, 단방향 1:N을 사용하는 것보다 @Embeddable을 사용하는게 나을수도 있지 않나?
1:N 매핑은 주문 상품에 대해 상세한 정보를 추적하고, 상품 자체에 대한 추가적인 관리가 필요할 때 사용한다.
@Embeddable은 주문과 주문 상품 간의 관계가 단순하고, 주로 읽기(read) 작업이 많을 때, 또는 데이터베이스 성능이 중요한 경우에 사용한다.
💡 매핑
장점
독립적인 관리: 각 엔티티가 독립적으로 관리될 수 있어, 개별적인 CRUD 작업이 가능하다.
확장성: 주문 상품에 대해 별도의 테이블을 사용하므로, 주문 상품의 속성이나 기능을 쉽게 확장할 수 있다.
관계 표현: 데이터베이스에서 부모-자식 관계를 명확하게 표현할 수 있습니다. 복잡한 쿼리와 조인 작업을 통해 다양한 데이터를 추출할 수 있다.
데이터 무결성: 데이터베이스의 외래 키 제약 조건을 통해 데이터 무결성을 보장할 수 있다.
단점
성능 이슈: 부모-자식 간의 조인 연산이 필요하므로, 성능에 영향을 미칠 수 있다.
복잡성: 데이터베이스 스키마와 애플리케이션 코드가 복잡해질 수 있다.
설정 및 관리: 엔티티 간의 관계를 설정하고 관리하는 데 추가적인 설정이 필요하다.
💡 @Embeddable
장점
단순성: 하나의 테이블에 모든 데이터를 저장하므로, 데이터 모델이 단순해진다.
성능: 조인 연산이 필요 없으므로, 조회 성능이 향상될 수 있다.
개발 용이성: 데이터베이스 스키마와 애플리케이션 코드가 단순해지고, 설정이 간편해진다.
단점
확장성 제한: 임베디드 객체의 속성이나 기능을 확장하는 데 한계가 있다.
데이터 중복: 동일한 데이터를 여러 레코드에 저장할 경우, 데이터 중복이 발생할 수 있다.
데이터 무결성 관리: 데이터베이스 수준에서의 무결성 제약 조건을 설정하기 어렵다.
복잡한 쿼리의 어려움: 임베디드 객체에 대한 복잡한 쿼리를 작성하기 어렵다.
💡 @Embeddable
엔티티가 아닌 타입을 한 개 이상의 필드와 매핑할 때 사용
엔티티의 한 속성으로 @Embeddable 적용 타입 사용
- JPA 1:N 매핑을 사용하기로 결정
왜?
현재는 한 주문에 대한 주문 상품을 보여주는 것으로 되어 있지만,
추후에는 주문 상품마다 배송 상태가 달라질 수도 있고. 주문 상품에 대해 가격, 수량 변경이 개별적으로 이루어져야 하기 때문에 1:N 매핑을 사용하기로 했다.
양방향 → 단방향 변경
RentalProducts Entity는 매핑에 관한 것은 삭제했다.
public class RentalEntity {
...
@OneToMany
@JoinColumn(name = "rentalproducts_id") // 외래 키를 설정
@OrderColumn(name = "order_no") // 리스트의 순서
private List<RentalProductsEntity> rentalProducts;
...
}
한 주문 내역에 주문 상품까지 나오는 것을 확인 할 수 있다.
참고 링크
- https://scoring.tistory.com/entry/JPA-양방향-연관관계와-연관관계의-주인-양방향-매핑-정리
- ⭐ JPA 연관 관계 한방에 정리 (단방향/양방향, 연관 관계의 주인, 일대일, 다대일, 일대다, 다대다)
- JPA 기초 16 엔티티 간 1-N 단방향 연관 매핑
- JPA 기초 06 @Embeddable
'Project > Collabo Project' 카테고리의 다른 글
[Villion] FullText Index를 활용한 도서 검색 기능 구현 (0) | 2024.08.15 |
---|---|
[Villion] 이 책과 함께 구매한 도서 목록 구현하기 (0) | 2024.08.14 |
[Villion] 동시성 문제 해결 방안 (2) | 2024.07.19 |
[Villion] MSA 구조 어떻게 해야 보기 좋을까..? (0) | 2024.07.12 |
[Villion] 찜한 상품 폴더 만들기(Gson 사용) (0) | 2024.06.25 |