객체의 경우 컬렉션이 존재하기 때문에 다대다 관계가 가능함.
하지만 관계형 DB는 컬렉션 관계를 양쪽에 가질 수 없기 때문에 다대다를 일대다 다대일로 풀어내는 중간 테이블이 필요함.
예를 들어서 Category 객체와 Item 객체를 자바로 작성한다고 하자.
Item은 여러 Category를 가질 수 있으며, Category는 여러 Item을 가질 수 있는 관계이므로 다대다 관계가 형성됨.
@Entity
public class Category {
@Id @GeneratedValue
private Long id;
private String name;
private List<Item> items = new ArrayList<>();
}
@Entity
public class Item {
@Id @GeneratedValue
private Long id;
private String name;
private List<Category> categories = new ArrayList<>();
}
컬렉션을 사용하면 하나의 Category에 여러 Item을 담을 수 있으며, 그 반대도 가능함.
그러므로 컬렉션이 존재하는 객체의 경우 다대다 관계 표현이 가능함.
@ManyToMany
와 @JoinTable
을 연관관계의 주인이라고 생각되는 곳에 넣으면 됨.
@Entity
public class Category {
@Id @GeneratedValue
private Long id;
private String name;
@ManyToMany
@JoinTable(name = "item_id")
private List<Item> items = new ArrayList<>();
}
@Entity
public class Item {
@Id @GeneratedValue
@Column(name = "item_id")
private Long id;
private String name;
}
@ManyToMany
는 양쪽에 넣되, @JoinTable
을 연관관계의 주인이라고 생각되는 곳에 넣고, 반대쪽에는 속성으로 mappedBy
를 넣어주면 됨.
@Entity
public class Category {
@Id @GeneratedValue
private Long id;
private String name;
@ManyToMany
@JoinTable(name = "item_id")
private List<Item> items = new ArrayList<>();
}
@Entity
public class Item {
@Id @GeneratedValue
@Column(name = "item_id")
private Long id;
private String name;
@ManyToMany(mappedBy = "items")
private List<Category> categories = new ArrayList<>();
}
연결 테이블이 연결'만' 하는 역할로 사용되며, 그 이상의 용도로 쓰이지 못함. -> 실무에서 쓸 이유가 없어짐.
연결 테이블(예시에서는 CategoryItem
)은 연결에만 쓰이는 용도가 아닐 수 있음.
예를 들어서 등록한 날짜, 수정한 날짜 등 CategoryItem
고유로 쓰이는 컬럼이 존재할 수밖에 없음.
연결 테이블용 엔티티를 따로 설계하는 것, 즉 연결 테이블을 엔티티 취급하는 것.
Category
와 Item
의 다대다 관계가 아닌, Category
와 CategoryItem
의 일대다 관계, Item
과 CategoryItem
의 일대다 관계로 보는 것.
@Entity
public class Category {
@Id @GeneratedValue
@Column(name = "category_id")
private Long id;
private String name;
@OneToMany(mappedBy = "category")
private List<Item> items = new ArrayList<>();
}
@Entity
public class CategoryItem {
@Id @GeneratedValue
@Column(name = "category_item_id")
private Long id;
@ManyToOne
@JoinColumn(name = 'category_id')
private Category category;
@ManyToOne
@JoinColumn(name = 'item_id')
private Item item;
private LocalDateTime createdDate;
}
@Entity
public class Item {
@Id @GeneratedValue
@Column(name = "item_id")
private Long id;
private String name;
@OneToMany(mappedBy = "item")
private List<Category> categories = new ArrayList<>();
}
CategoryItem
엔티티를 설계하지 않아도 연결용으로만 연결하는 것이라면 @ManyToMany
와 @JoinTable
만으로 가능함.
@Entity
public class Category {
@Id
@GeneratedValue
@Column(name = "category_id")
private Long id;
private String name;
@ManyToMany
@JoinTable(name = "category_item",
joinColumns = @JoinColumn(name = "category_id"),
inverseJoinColumns = @JoinColumn(name = "item_id")
)
private List<Item> items = new ArrayList<>();
}
@Entity
public abstract class Item {
@Id @GeneratedValue
@Column(name = "item_id")
private Long id;
private String name;
@ManyToMany(mappedBy = "items")
private List<Category> categories = new ArrayList<>();
}
@ManyToMany
@JoinTable(name = "category_item",
joinColumns = @JoinColumn(name = "category_id"),
inverseJoinColumns = @JoinColumn(name = "item_id")
)
private List<Item> items = new ArrayList<>();
@JoinTable
: 데이터베이스 테이블 간의 관계를 정의할 때 사용됨.name = "category_item"
: 생성될 테이블의 이름을 category_id
로 지정하라.joinColumns = @JoinColumn(name = "category_id")
: CategoryItem
의 category_id
를 현재 클래스의 인스턴스와 CategoryItem
테이블이 조인될 때 사용할 열로 지정하라.CategoryItem
에 들어가는 category_id
를 말하는 것.inverseJoinColumns = @JoinColumn(name = "item_id")
: Item
의 item_id
를 다른 클래스의 인스턴스와 CategoryItem
테이블이 조인될 때 사용할 열로 지정하라.CategoryItem
에 들어가는 item_id
를 말하는 것.하지만 지양해야 할 것은 @ManyToMany
그 자체이기 때문에 되도록이면 일대다 다대일로 풀어서 사용할 것.