다대다 연관관계를 회원(Member)과 상품(Product)을 예시로 설명해보도록 하겠습니다.
JPA 에서 @ManyToMany 를 통해 다대다 연관관계를 제공하지만, 결론은 사용하지 않는 것 을 추천합니다.
그렇다면, 다대다 연관관계는 어떻게 객체로 표현할 수 있을까요??
- 연결 테이블(조인 테이블)을 추가하여 일대다, 다대일 관계로 풀어내야 합니다.
관계형 데이터베이스는 정규화된 테이블 2개로 다대다 관계를 표현할 수 없습니다.
객체는 컬렉션을 사용해서 객체 2개로 다대다 관계를 표현할 수 있습니다.
@ManyToMany 를 사용한 다대다 연관관계 매핑 방법을 살펴보며, 왜 사용하지 않는 것 을 추천하는지 알아보도록 하겠습니다.
Member 클래스 (다대다 단방향
연관관계의 주인입니다.)
@Entity
public class Member {
@Id
@Column(name = "MEMBER_ID")
private Long id;
@ManyToMany
@JoinTable(name = "member_product,
joinColumns = @JoinColumn(name = "MEMBER_ID"),
inverseJoinColumn = @JoinColumn(name = "PRODUCT_ID"))
private List<Product> products = new ArrayList<>();
@Column(name = "USERNAME")
private String username;
// Getter, Setter, Constructor
}
Produdct 클래스
@Entity
public class Product {
@Id
@Column(name = "PRODUCT_ID")
private Long id;
@Column(name = "NAME")
private String name;
}
다대다 단방향
매핑에서는 연관관계의 주인
에 해당하는 클래스에 반대편 클래스를 Collection 프레임워크로 감싼 형태의 참조 필드로 작성해주시면 됩니다.
이때, 해당 참조 필드위에 @ManyToMany
과 @JoinTable(name = "연결 테이블 이름")
을 추가하여 줍니다.
다대다 관계에서 사용합니다.
조인 테이블을 생성합니다.
속성 | 기능 | 기본값 |
---|---|---|
mappedBy | 연관관계의 주인 필드를 선택한다. | |
fetch | 글로벌 패치 전략을 설정한다. (자세한 내용은 추후에) | FetchType.LAZY (지연 로딩) |
cascade | 영속성 전이 기능을 사용한다. (자세한 내용은 추후에) | |
targetEntity | 연관된 엔티티의 타입 정보를 설정한다. 이 기능은 거의 사용하지 않음 |
연결 테이블을 매핑할 때 사용합니다.
속성 | 기능 | 기본값 |
---|---|---|
name | 연결 테이블의 이름을 지정한다. | |
joinColumns | 연결 테이블에 존재하는 다대다 연관관계의 주인에 해당하는 외래키를 매핑한다. | |
inverseJoinColumns | 연결 테이블에 존재하는 다대다 연관관계의 주인이 아닌 객체에 해당하는 외래키를 매핑한다. | |
@Entity
public class Member {
@Id
@Column(name = "MEMBER_ID")
private Long id;
@ManyToMany
@JoinTable(name = "member_product,
joinColumns = @JoinColumn(name = "MEMBER_ID"),
inverseJoinColumn = @JoinColumn(name = "PRODUCT_ID"))
private List<Product> products = new ArrayList<>();
@Column(name = "USERNAME")
private String username;
// Getter, Setter, Constructor
}
Produdct 클래스
@Entity
public class Product {
@Id
@Column(name = "PRODUCT_ID")
private Long id;
@ManyToMany(mappedBy = "products")
private List<Member> members = new ArrayList<>();
@Column(name = "NAME")
private String name;
// Getter, Setter, Constructor
}
다대다 양방향
매핑에서는 다대다 단방향
코드에 추가로 연관관계의 주인이 아닌
클래스에 반대편 클래스(연관관계 주인)를 Collection 프레임워크로 감싼 형태의 참조 필드로 작성해주시면 됩니다.
이때, 해당 참조 필드위에 @ManyToMany(mappedBy = "반대쪽 매핑의 필드 이름값")
을 추가하여 연관관계의 주인을 지정하여 줍니다.
@ManyToMany 를 사용하면 편리한 점
하지만 실무에서 @ManyToMany 매핑은 사용하기에는 한계가 있습니다.
연결 테이블용 엔티티를 생성 후,
@ManyToMany 를 @ManyToOne 과 @OneToMany 를 활용하여 일대다, 다대일 관계로 풀어줍니다.
ORDER
는 예약어 이기 때문에, 테이블 이름을 수정해주어야 합니다. (예 - ORDERS)즉, 위와같이 @ManyToMany 매핑을 @ManyToOne 과 @OneToMany 를 사용하여 코드를 작성합니다.
Member 클래스 (기존 다대다 단방향
매핑 시, 연관관계의 주인 입니다.)
@Entity
public class Member {
@Id
@Column(name = "MEMBER_ID")
private Long id;
// 연결 테이블(MEMBER_PRODUCT)쪽이 외래키를 갖고있기 때문에, 연결 테이블이 연관관계의 주인이다.
@OneToMany(mappedBy = "member")
private List<MemberProduct> memberProducts = new ArrayList<>();
@Column(name = "USERNAME")
private String username;
// Getter, Setter, Constructor
}
Produdct 클래스
@Entity
public class Product {
@Id
@Column(name = "PRODUCT_ID")
private Long id;
@Column(name = "NAME")
private String name;
// Getter, Setter, Constructor
}
MemberProduct 클래스 (다대다
에서 연결 테이블)
@Table(name = "ORDERS")
@Entity
public class MemberProduct {
@Id
@Column(name = "ORDER_ID")
private Long id;
@ManyToOne
@JoinColumn(name = "MEMBER_ID")
private Member member;
@ManyToOne
@JoinColumn(name = "PRODUCT_ID")
private Product product;
@Column(name = "ORDERAMOUNT")
private Integer orderAmount;
@Column(name = "ORDERDATE")
private LocalDateTime orderDate;
// Getter, Setter, Constructor
}
연관관계 사용에 대한 예시 코드는 아래 링크를 참고해주시기 바랍니다.
소스 코드
Member 클래스
@Entity
public class Member {
@Id
@Column(name = "MEMBER_ID")
private Long id;
// 연결 테이블(MEMBER_PRODUCT)쪽이 외래키를 갖고있기 때문에, 연결 테이블이 연관관계의 주인이다.
@OneToMany(mappedBy = "member")
private List<MemberProduct> memberProducts;
@Column(name = "USERNAME")
private String username;
// Getter, Setter, Constructor
}
Produdct 클래스
@Entity
public class Product {
@Id
@Column(name = "PRODUCT_ID")
private Long id;
// 연결 테이블(MEMBER_PRODUCT)쪽이 외래키를 갖고있기 때문에, 연결 테이블이 연관관계의 주인이다.
@OneToMany(mappedBy = "product")
private List<MemberProduct> memberProducts;
@Column(name = "NAME")
private String name;
// Getter, Setter, Constructor
}
MemberProduct 클래스 (다대다
에서 연결 테이블)
@Table(name = "ORDERS")
@Entity
public class MemberProduct {
@Id
@Column(name = "ORDER_ID")
private Long id;
@ManyToOne
@JoinColumn(name = "MEMBER_ID")
private Member member;
@ManyToOne
@JoinColumn(name = "PRODUCT_ID")
private Product product;
@Column(name = "ORDERAMOUNT")
private Integer orderAmount;
@Column(name = "ORDERDATE")
private LocalDateTime orderDate;
// Getter, Setter, Constructor
}
연관관계 사용에 대한 예시 코드는 아래 링크를 참고해주시기 바랍니다.
소스 코드