JPA의 일대다, 다대일 관계에 대해 알아보자.
일대다(1:N)
데이터베이스에서 일대다 관계는 굉장히 다양한 경우에 적용될 수 있다.
예를 들어, 장바구니와 담긴 상품, 교실과 학생, 게시판과 게시글 등이 있다.
이런 관계를 구현하기 위해 @OneToMany 어노테이션을 이용하여 다음과 같이 구현할 수 있다.
다음은 주문과 상품의 관계에 대한 일대다 양방향 관계를 나타낸 코드이다.
@Entity
public class Order {
// ...
@OneToMany(mappedBy = "order")
private List<Product> products = new ArrayList<>();
// ...
}
@Entity
public class Product {
// ...
@ManyToOne
@JoinColumn(name = "order_id")
private Order order;
// ...
}
위 코드에서 연관관계의 주인은 Product 엔티티가 된다. 연관관계의 주인이 아닌 쪽은 mappedBy를 통해서 연관관계의 주인을 지정해준다.
일대다(1:N) 관계 사용의 한계
일대다(1:N) 양방향 매핑은 존재하지 않으며, 해당 관계는 표준 스펙에서 지원은 하지만 실무에서 권장하지 않는 관계이다.
양방향 매핑에서 @OneToMany는 주인이 될 수 없기 때문이다, 쉽게 말해 FK는 항상 N쪽에 있기에 일대다의 양방향 매핑은 이루어질 수 없다.
위 객체, 테이블 모델링을 보면 Member 테이블이 TEAM_ID라는 외래키를 가지고 있는것과 반대로 객체에서는 Team 엔티티가 외래키를 가지고 있는 모습을 확인할 수 있다.
- 테이블 모델링에서 외래키는 항상 다(N)쪽 테이블에 존재한다.
- Member 엔티티에는 외래 키를 매핑할 수 있는 참조 필드가 없다.
뭔가 모순과 헷갈림이 공존하는걸 느낄 수 있다.
일대다 관계는 성능상에도 이슈가 발생한다.
다음은 하나의 주문에 N개의 상품을 넣어 저장하는 코드이다.
Product product1 = new Product(0L, "사과");
Product product2 = new Product(1L, "배");
entityManager.persist(product1);
entityManager.persist(product2);
Order order = new Order(0L, "주문");
order.getProducts().add(product1);
order.getProducts().add(product2);
entityManager.persist(order);
위 코드가 실행된 SQL은 다음과 같다.
INSERT INTO PRODUCT (PRODUCT_ID, PRODUCTNAME) VALUES (0, '사과');
INSERT INTO PRODUCT (PRODUCT_ID, PRODUCTNAME) VALUES (1, '배');
INSERT INTO ORDER (ORDER_ID, NAME) VALUES (0, '주문');
UPDATE PRODUCT SET ORDER_ID = 0 WHERE PRODUCT_ID = 0;
UPDATE PRODUCT SET ORDER_ID = 0 WHERE PRODUCT_ID = 1;
일대다 관계에서 저장이 이루질때 Product는 Order의 존재를 모른다.( 양방향이 아닌 단방향만 가능하기 때문)
Order 엔티티가 연관관계의 주인으로써 Product를 관리하기 때문에 Order 엔티티가 저장될때, Product 엔티티에는 Product 엔티티의 ORDER_ID 외래 키에 아무 값도 저장되지 않는다.
대신 Order 엔티티가 저장될 때 Order 엔티티가 가지고 있는 Products 값들을 참고하여 Product의 ORDER_ID 외래 키 값을 업데이트한다. (다대일 관계를 통해서는 발생하지 않을 불필요한 Update 구문 발생)
일대다 관계를 사용할 경우, 실제 외래키가 존재하는 DB 테이블과 다른구조를 사용하게 됨으로 복잡성이 증가하며 불필요한 Update 쿼리의 발생으로 성능상의 불이익이 발생한다.
그러므로, 일대다 단방향 매핑 대신 다대일 양방향 매핑을 통해 이러한 문제를 해소하자.
다대일(N:1)
일대다 관계와 다대일 관계는 언뜻 보기에는 같아보일 수 있다.
두 연관관계의 차이점은 '연관관계의 주인'을 어디에 두느냐에 대한 차이로 구분지어 진다.
일대다 관계를 다대일 관계로 바꾸려면 Order 엔티티가 여러 개의 상품을 가질 수 있는 대신, 상품 엔티티가 하나의 주문에 속하는 형태로 변경된다.
다음은 주문과 상품에 대한 양방향 다대일 관계를 구현한 코드이다.
@Entity
public class Order {
// ...
// 다대일 관계 설정
@OneToMany(mappedBy = "product")
private List<Order> orders = new ArrayList<>();
// ...
}
@Entity
public class Order {
// ...
@ManyToOne
@JoinColumn(name = "product_id")
private Product product;
// ...
}
'Spring' 카테고리의 다른 글
IntelliJ에서 Spring MVC 세팅하기 (0) | 2024.03.17 |
---|---|
[Spring] Spring Boot 3 마이그레이션 시작하기 (0) | 2023.12.13 |
[JPA] 연관관계의 주인 (0) | 2023.09.01 |
[Spring] 서비스 운영간 발생한 DB Connection Closed 이슈 (0) | 2023.08.30 |
[Spirng, JPA] object references an unsaved transient instance - save the transient instance before flushing (0) | 2023.08.16 |