JPA 연관관계 다대다(N:M)
- 실무에선 보통 사용하지 않는것을 권장한다.
- 관계형 데이터베이스에서 정규화된 2개의 테이블로 다대다를 표현할 수 없다.
- 관계형 데이터베이스에서 다대다를 표현하기 위해서는 중간 테이블을 통해 일대다, 다대일로 나누어서 표현해야한다.
- JPA에서는 관계형 데이터베이스가 중간 테이블을 만드는 것과 달리 @ManyToMany 어노테이션을 통해 별도의 중간 테이블에 해당하는 엔티티를 만들지 않고도 다대다를 표현할 수 있다.
단방향 다대다
다대다 관계를 JPA를 통한 단방향 구현시 아래와 같이 코드 작성이 가능하다.
Member (연관관계의 주인)
@Entity
@Table(name = "member")
@Getter
public class MemberEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
private Long memberId;
@ManyToMany(cascade = CascadeType.ALL)
@JoinTable(
name = "member_product",
joinColumns = @JoinColumn(name = "memberId"),
inverseJoinColumns = @JoinColumn(name = "productId")
)
private List<ProductEntity> products = new ArrayList<>();
@Column(name = "user_name")
private String userName;
}
Product
@Entity
@Table(name = "product")
@Getter
public class ProductEntity {
@Id
@Column(name = "id") @GeneratedValue(strategy = GenerationType.IDENTITY)
private String productId;
private String name;
}
양방향 다대다
다대다 연관관계를 JPA를 통한 양방향 구현시 아래와 같이 코드 작성이 가능하다.
단방향과 대부분의 코드가 같지만, 자식 엔티티에 @ManyToMany와 mappedBy를 통해 참조필드를 만들어준다.
Member
@Entity
@Table(name = "member")
@Getter
public class MemberEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
private Long memberId;
@ManyToMany(cascade = CascadeType.ALL)
@JoinTable(
name = "member_product",
joinColumns = @JoinColumn(name = "memberId"),
inverseJoinColumns = @JoinColumn(name = "productId")
)
private List<ProductEntity> products = new ArrayList<>();
@Column(name = "user_name")
private String userName;
}
Product
@Entity
@Table(name = "product")
@Getter
public class ProductEntity {
@Id
@Column(name = "id") @GeneratedValue(strategy = GenerationType.IDENTITY)
private String productId;
@ManyToMany(mappedBy = "products")
private List<MemberEntity> members = new ArrayList<>();
private String name;
}
헷갈릴 수 있는 부분은, JPA에서 @JoinTable을 이용해서 실제 데이터베이스에 존재하지 않은 테이블을 만드는 것은 아니다. 그렇기 때문에 중간 테이블을 명시하고 사용하고자 하면 물리 데이터베이스에 중간 테이블을 생성해주어야 한다.
다대다(N:M) 관계를 실무에서 사용하면 안되는 이유
- JPA의 다대다 관계를 이용할 경우 중간 테이블이 생성되게 된다. 이 때 중간 테이블은 연결된 각각의 테이블에 대한 키값을 가지고 있을 뿐 실무레벨에서 필요한 여러 필수정보를 가질 수 없다(ex. 데이터 생성시간, 수정시간 등)
- 실제 DB의 물리 테이블에 존재하는 컬럼 데이터에 접근 및 핸들링 할 수 없다.
- JPA사용시 엔티티를 통해 테이블을 확인, 관리 할 수 있는 것과 달리 다대다 관계로 생성된 중간 테이블은 엔티티를 통해 확인할 수 없다.
다대다(N:M) 관계를 풀어서 해결하는 방법
- 다대다 관계를 사용하고 싶을 경우, 중간 테이블에 해당하는 엔티티를 만들고 @OneToMany, @ManyToOne으로 풀어서 사용해야 하는것을 추천한다.
'Spring' 카테고리의 다른 글
[Spirng, JPA] object references an unsaved transient instance - save the transient instance before flushing (0) | 2023.08.16 |
---|---|
[Spring] 구글 OAuth2.0 이용 소셜 로그인 구현 (0) | 2023.08.10 |
[JPA]연관관계 - 일대일 관계 (0) | 2023.07.27 |
[JPA] 연관관계 조회 방식별(Fetch, Lazy) 성능 차이 테스트 (0) | 2023.07.25 |
[Spring] MapStruct 사용 주의 사항 (0) | 2023.07.25 |