연관관계 매핑
RDBMS를 사용할 때는 테이블 하나만 사용해서 애플리케이션의 모든 기능을 구현하는 것은 어렵다.
JPA를 사용하는 애플리케이션에서도 테이블의 연관관계를 엔티티 간의 연관관계로 표현이 가능하다.
그 중에서 다대다(N:N) 매핑 방식에 대해 알아보겠습니다.
연관관계 매핑 종류와 방향
- Many To Many : 다대다(N:N)
- 실무에서는 거의 사용되지 않는 구성이다.
- 예시) 상품과 생산 업체 : 한 종류의 상품이 여러 생산업체를 통해 생산될 수도 있고, 생산업체 한 곳이 여러 상품을 생산할 수도 있다.
- 다대다 연관관계에서는 각 엔티티에서 서로를 리스트로 가지는 구조로 만들어진다.
- '교차 엔티티' 라고 부르는 중간테이블을 생성하여 다대다 관계를 일대다 또는 다대일 관계로 해소시킨다.
다대다 단방향 매핑
연관관계를 가진 생산업체 엔티티를 생성
생산업체 엔티티
public class Producer extends BaseEntity{
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String code;
private String name;
@ManyToMany
@ToString.Exclude
private List<Product> productList = new ArrayList<>();
public void addProduct(Product product) {
productList.add(product);
}
}
- 다대다 연관관계는 @ManyToMany 어노테이션으로 설정한다.
- 리스트로 필드를 가지는 개체에서는 외래키를 가지지 않기에 별도의 @JoinColumn은 설정하지 않아도 된다.
리포지토리 생성
- 생성한 생산업체 엔티티를 활용할 수 있도록 리포지토리를 생성한다.
@Repository
public interface ProducerRepository extends JpaRepository<Producer, Long> {
}
다대다 단방향 연관관계 테스트
@Test
@Transactional
void relationshipTest() {
Product product1 = savedProduct("펜", 1000, 100);
Product product2 = savedProduct("가방", 10000, 1000);
Product product3 = savedProduct("가위", 5000, 500);
Producer producer1 = savedProducer("flature");
Producer producer2 = savedProducer("wikibooks");
producer1.addProduct(product1);
producer1.addProduct(product2);
producer2.addProduct(product1);
producer2.addProduct(product2);
producerRepository.saveAll(Lists.newArrayList(producer1, producer2));
System.out.println(producerRepository.findById(1L).get().getProductList());
}
private Producer savedProducer(String name) {
Producer producer = new Producer();
producer.setName(name);
return producerRepository.save(producer);
}
private Product savedProduct(String name, int price, int stock) {
return productRepository.save(Product.builder()
.name(name)
.price(price)
.stock(stock)
.build());
}
- 가독성을 위해서 리포지토리를 통한 테스트 케이스를 별도의 메서드로 구현하였다.
- 리포지토리를 사용하게 되면 매번 트랜잭션이 끊어져 리스트를 가져오는 작업이 불가능하다.
- 테스트 메서드에 @Transactional 어노테이션을 지정하여 트랜잭션이 유지되도록 구성하여 테스트를 진행한다.
다대다 양방향 매핑
상품 엔티티에서 생산업체 엔티티 연관관계 설정
- 필요에 따라 MappedBy 속성을 이용하여 두 엔티티 간의 연관관계 주인을 설정할 수도 있다.
- 중간 테이블이 연관관계를 설정하고 있기에, 데이터베이스의 테이블 구조는 변경되지 않는다.
public class Product extends BaseEntity{
//..중략
@ManyToMany
@ToString.Exclude
private List<Producer> producers = new ArrayList<>();
public void addProducer(Producer producer) {
this.producers.add(producer);
}
}
다대다 양방향 연관관계 테스트
@Test
@Transactional
void relationshipTest2() {
Product product1 = savedProduct("펜", 1000, 100);
Product product2 = savedProduct("가방", 10000, 1000);
Product product3 = savedProduct("가위", 5000, 500);
Producer producer1 = savedProducer("feature");
Producer producer2 = savedProducer("wiki-books");
producer1.addProduct(product1);
producer1.addProduct(product2);
producer2.addProduct(product2);
producer2.addProduct(product3);
product1.addProducer(producer1);
product2.addProducer(producer1);
product2.addProducer(producer2);
product3.addProducer(producer2);
producerRepository.saveAll(Lists.newArrayList(producer1, producer2));
productRepository.saveAll(Lists.newArrayList(product1, product2, product3));
System.out.println("products : " + productRepository.findById(1L)
.get().getProducers());
System.out.println("products : " + producerRepository.findById(1L)
.get().getProductList());
}
- 다대다 연관관계를 설정하면 중간 테이블을 통해 연관된 엔티티의 값을 가져올 수 있다.
- 다대다 연관관계에서는 중간 테이블이 생성되기 때문에 예기치 못한 쿼리가 생길 수 있다.
- 관리하기 힘든 포인트가 발생하기도 하는데, 이러한 한계를 극복하기 위해 중간 테이블을 생성하는 대신에 일대다 또는 다대일로 연관관계를 맺을 수 있는 중간 엔티티로 승격시켜 JPA에서 관리할 수 있게 생성하는 것이 좋다.
반응형
'Book > 스프링부트 핵심가이드' 카테고리의 다른 글
영속성 전이와 고아 객체 (0) | 2023.11.15 |
---|---|
연관관계 매핑(of. 다대일(N:1), 일대다(1:N) 매핑) (0) | 2023.11.14 |
연관관계 매핑 (of. 일대일(1:1) 매핑) (0) | 2023.11.14 |
댓글