- 참조: 자바 ORM 표준 JPA 프로그래밍
- M : N 관계
회원과 상품이 있다고 가정해보자. 회원은 여러 상품을 주문할 수 있고, 하나의 상품도 여러 회원들에 의해 주문될 수 있다. 이때 다중성의 관계는 M : N 이다.
이를 ER Diagram 으로 나타내면 위와 같다. 그러나 RDB(관계형 DB)는 정규화된 테이블 2개로 다대다 관계를 표현할 수 없다. 그래서 조인 테이블을 사용한다.
1 번째 다이어그램에서 USER 와 PRODUCT 의 PK인 USER_ID 와 PRODUCT_ID 로 이루어진 조인테이블 USER_PRODUCT 가 추가되었다.
하지만 객체의 경우에는 서로에 대해 M : N 을 나타낼 때 조인객체를 두지 않는다.
위 다이어그램처럼 다대다 관계에서 회원은 상품을, 상품은 회원을 컬렉션으로 참조하면 된다.
- M : N 단방향
다대다 단방향 관계인 회원과 상품을 JPA 코드로 표현해보자. Product 엔티티는 특별한 맵핑없이 선언하면 된다.
@Entity
public class Product {
@Id
@Column(name = "PRODUCT_ID")
private String id;
private String name;
다대다 단방향 맵핑을 하는 User 엔티티를 살펴보자.
@Entity
public class User {
@Id
@Column(name = "USER_ID")
private Long id;
private String userName;
@ManyToMany
@JoinTable(name = "USER_PRODUCT",
joinColumns = @JoinColumn(name = "USER_ID"),
inverseJoinColumns = @JoinColumn(name = "PRODUCT_ID"))
private List<Product> products = new ArrayList<>();
다중성은 다대다이므로 @ManyToMany 어노테이션을 사용한다. 조인 어노테이션은 일대다 관계에서 사용한 @JoinColumn 이 아닌 @JoinTable 어노테이션을 사용한다. 이때 @JoinTable 안에 사용하는 속성의 사용법은 아래와 같다.
- name: 다대다 맵핑관계의 조인 관계테이블 명
- joinColumns: User 와 맵핑할 조인컬럼 정보를 지정한다.
- inverseJoinColumns: User 와 반대방향인 Product 와 맵핑할 조인컬럼 정보를 지정한다.
실행을 하면 @JoinTable 어노테이션 정보를 읽어서 아래와 같이 테이블이 생성한다.
만약 JPA 가 아닌 Mybatis 를 사용하면 조인 테이블을 SQL 마다 명시해서 조회해야 겠지만 JPA 는 조인테이블의 존재를 명시하지 않아도 객체 그래프를 참조하는것처럼 사용하면 된다. 회원과 상품을 저장후 탐색하는 코드를 작성해보자.
public static void main(String[] args) {
SpringApplication.run(JpaApplication.class, args);
EntityManager em = emf.createEntityManager();
save(em);
EntityManager em1 = emf.createEntityManager();
find(em1);
emf.close();
}
public static void save(EntityManager em) {
EntityTransaction tx = em.getTransaction();
tx.begin();
Product product = new Product();
product.setId("PRODUCT#1");
product.setName("Product Name#1");
em.persist(product);
User user = new User();
user.setId("USER#1");
user.setUserName("User Name#1");
user.setProducts(Arrays.asList(product));
em.persist(user);
tx.commit();
em.close();
}
public static void find(EntityManager em) {
EntityTransaction tx = em.getTransaction();
tx.begin();
User user = em.find(User.class, "USER#1");
user.getProducts()
.forEach(System.out::println);
tx.commit();
em.close();
}
find 메소드에서 console 에는 아래와 같은 로그가 남는다.
Hibernate:
select
products0_.user_id as user_id1_8_0_,
products0_.product_id as product_2_8_0_,
product1_.product_id as product_1_5_1_,
product1_.name as name2_5_1_
from
user_product products0_
inner join
product product1_
on products0_.product_id=product1_.product_id
where
products0_.user_id=?
com.example.demo.test.Product@1ddf42dd
SQL from 절에 user_product 에서 user_id 로 검색한다음 product 를 join 하였다. 그리고 Sysout 으로 작성한 부분에는 인스턴스의 주소가 찍힌다.
- M : N 양방향
M : N 단방향을 알아보았으니 M : N 양방향에 대해서 알아보자. 상품에서도 회원을 컬렉션으로 참조한다.
@Entity
public class Product {
@Id
@Column(name = "PRODUCT_ID")
private String id;
private String name;
@ManyToMany(mappedBy = "products")
private List<User> users;
다중성은 다대다이므로 @ManyToMany 를 사용한다. 연관관계의 주인은 User 엔티티이므로 Product 엔티티에서 mappedBy 속성을 사용한다. User 엔티티에서 Product 를 참조하는 필드이름을 지정해준다.
이번에는 Product 에서 Uesr 를 참조하는 예제를 살펴보자.
public static void findInverse(EntityManager em) {
EntityTransaction tx = em.getTransaction();
tx.begin();
Product product = em.find(Product.class, "PRODUCT#1");
List<User> users = product.getUsers();
System.out.println("====findInverse====");
users.forEach(user -> {
System.out.println("User name: " + user.getUserName());
});
tx.commit();
em.close();
}
User 엔티티에 제품을 추가하는 편의기능 메소드를 작성해준다. User 엔티티에서 컬렉션으로 제품목록을 참조하는 products 필드는 기본 setter 메소드로 작성하면 List 를 추가해야 한다. 단건 제품을 추가하는 편의 메소드를 추가해주자.
public void addProduct(Product product) {
this.products.add(product);
product.getUsers().add(this);
}
실행결과는 아래와 같다. user_product 에서 제품을 찾고 user 를 조인하여 조회하는 SQL 이 수행된다.
====findInverse====
Hibernate:
select
users0_.product_id as product_2_8_0_,
users0_.user_id as user_id1_8_0_,
user1_.user_id as user_id1_7_1_,
user1_.user_name as user_nam2_7_1_
from
user_product users0_
inner join
user user1_
on users0_.user_id=user1_.user_id
where
users0_.product_id=?
User name: User Name#1
'Framework and Tool > JPA' 카테고리의 다른 글
JPA - 다양한 연관관계 - M : N 비식별관계 (0) | 2021.07.10 |
---|---|
JPA - 다양한 연관관계 - M : N 식별관계 (0) | 2021.07.10 |
JPA - 다양한 연관관계 - 1 : 1 (0) | 2021.07.06 |
JPA - 다양한 연관관계 - N : 1 과 1 : N (0) | 2021.07.03 |
JPA - 연관관계 맵핑 - 연관관계 맵핑 (0) | 2021.07.02 |
댓글