-
[JPA] ID 참조와 조인 테이블을 이용한 단방향 M:N 매핑스프링프레임워크/jpa 2021. 1. 14. 17:19
이번 포스트에서는 ID 참조를 이용해서 엔티티 간 단방향 M:N 연관 관계를 해결하는 방법을 알아보도록 하겠습니다.
Product와 Category의 관계가 아래와 같다고 가정합니다.
- Product와 Category는 M:N 연관관계이다.
- 특정 Category에 있는 Product 정보 조회 기능이 필요하다.
- 특정 Product가 속해 있는 Category 정보 조회 기능은 필요없다.
위와 같은 가정하에서는 Product와 Category는 양방향 매핑 관계를 가질 필요가 없게되고, 단방향 매핑을 통해서 요구 사항을 만족 시킬 수 있게 됩니다.
이 때 엔티티간에 연관 관계를 맺을 수도 있지만 아래와 같이 ID 참조를 이용해서 더 간편하게 표현 할 수 있습니다.
우선 Product 클래스를 아래와 같이 구성합니다.
- @CollectionTable의 joinColumns 속성을 사용해서 Product에서 product_category를 참조할 때 사용하는 조인 컬럼을 지정한다.
- Category와의 연관 관계를 엔티티가 아닌 Category의 ID를 참조하게 합니다.
@NoArgsConstructor(access = AccessLevel.PROTECTED) @Getter @Entity public class Product { @EmbeddedId private ProductId id; @ElementCollection @CollectionTable(name = "product_category", joinColumns = @JoinColumn(name = "product_id")) private Set<CategoryId> categoryIds = new HashSet<>(); private String name; private Money price; private String detail; @OneToMany(cascade = {CascadeType.PERSIST, CascadeType.REMOVE, CascadeType.MERGE}, orphanRemoval = true, fetch = FetchType.EAGER) @JoinColumn(name = "product_id") @OrderColumn(name = "list_idx") private List<Image> images = new ArrayList<>(); public Product(ProductId id, String name, Money price, String detail, List<Image> images) { this.id = id; this.name = name; this.price = price; this.detail = detail; this.images.addAll(images); } }
public interface ProductRepository extends JpaRepository<Product, ProductId> { }
다음으로 Product 엔티티와 연관 관계가 있는 Category 엔티티를 생성합니다.
@NoArgsConstructor(access = AccessLevel.PROTECTED) @Getter @Entity public class Category { @EmbeddedId CategoryId id; String name; public Category(CategoryId id, String name) { this.id = id; this.name = name; } }
interface CategoryRepository extends JpaRepository<Category, CategoryId> { }
위와 같이 엔티티를 생성하고 테스트를 진행해보도록 하겠습니다.
@Rollback(false) @Transactional @SpringBootTest @TestMethodOrder(OrderAnnotation.class) public class ProductTest { @Autowired ProductRepository productRepository; @Autowired CategoryRepository categoryRepository; @Test @Order(1) public void 카테고리_등록() { CategoryId categoryId = new CategoryId(1l); Category category = new Category(categoryId, "가전"); Category savedCategory = categoryRepository.save(category); assertThat(savedCategory.getId()).isEqualTo(category.getId()); } @Test @Order(2) public void 상품_등록() { Product product = new Product(new ProductId("prod-999"), "999", new Money(999), "detail", Arrays.asList(new ExternalImage("externalPath", "uchupura"), new InternalImage("internal1", true), new InternalImage("internal2", false))); product.getCategoryIds().add(new CategoryId(1l)); productRepository.save(product); } @Test @Order(3) public void 특정_카테고리에_존재하는_상품리스트_검색() { List<Product> products = productQueryRepository.findByCategoryId(new CategoryId(1l)); products.forEach(product -> System.out.println(product.getName())); } @Test @Order(4) public void 특정_상품_검색() { Product product = productQueryRepository.findById(new ProductId("prod-999")); System.out.println(product.getName()); } }
위 테스트를 실행 시 데이터베이스에 입력 된 내용입니다.
이상으로 ID 참조를 이용해서 엔티티 간 단방향 M:N 연관 관계를 매핑하는 방법에 대해서 알아보았습니다.
'스프링프레임워크 > jpa' 카테고리의 다른 글
[JPA] @ElementCollection을 이용해서 Embeddable 타입의 Collection을 영속화 (0) 2021.01.29 [JPA] 영속성 컨텍스트 (0) 2021.01.13 [JPA] JPA 관련 유용한 블로그 글 모음 (0) 2021.01.12 [JPA] QueryDsl에서 Pageable 객체를 이용한 Sort 방법 (6) 2020.09.09