이렇게 카테고리와 아이템을 연결할때 서로 다대다 연결이라고한다면, 중간 테이블을 만들어 연결한다고한다. 저번 포스팅에서 알 수 있듯이 다대일 과 일대다 관계는 양방향 연결일 때, 어디에 맵핑되는지 설정을 해주어야 한다. 분명 '다'인 외래키에 연관관계 주인역할을 선언해주어야한다고 했는데, 다대다 관계는 양쪽이 '다'관계이다.
다대다 관계일때 연관관계 주인을 직접 설정해 줘야하는데, 이때 몇가지 규칙이 존재한다.
이 세가지 규칙을 이용해 연관관계 주인을 잘 설정해야한다.
@Entity
@Getter @Setter
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<>();
@ManyToOne(fetch = LAZY)
@JoinColumn(name = "parent_id")
private Category parent;
@OneToMany(mappedBy = "parent")
private List<Category> child = new ArrayList<>();
}
다대다 관계에서 연관관계 주인은 @JoinTable을 사용해야한다. 여기서 joinColumns는 중간 테이블의 카테고리를 inverseJoinColumns에는 중간테이블의 아이템을 설정해주어야한다.
이렇게 설정해 준 반대편에는 일대다 관계와 동일하게 @mappedBy(name={맵핑될객체})로 설정해주면 된다.
보통 책에는 카테고리가 존재하는데 카테고리는 트리형태로 내려가게된다. 하나의 부모 카테고리 안에 여러개의 자식 카테고리가 있다는 뜻이다. 이런경우 어떻게 연관관계를 설정해줘야할까?
@ManyToOne(fetch = LAZY)
@JoinColumn(name = "parent_id")
private Category parent;
@OneToMany(mappedBy = "parent")
private List<Category> child = new ArrayList<>();
저번 포스팅과 동일하게 하나의 클래스안에서 이렇게 ManyToOne과 OneToMany를 설정해주면된다.
Setter가 모두 열려있다면, 변경포인트가 너무 많아 유지보수가 어렵다. 그래서 오로지 Gatter만 사용해야하며, 값의 수정보다는 새로운 객체를 생성해 값을 추가해야한다.
JPA 구현 라이브러리가 객체를 생성할 때, 리플렉션 같은 기술을 사용할 수 있어야 하기 때문에 이렇게 설정해주어야한다.
즉시로딩은 최악의 경우 하나 가져올때 모든 연관관계를 다 끌고오게된다 그래서 지연로딩인 Lazy로 설정해주어야한다. 즉시로딩은 실무에서 절대 사용하면 안된다.
@ManyToOne과 같이 "xToOne"의 형태는 즉시로딩이 기본값이다. 때문에 항상 @ManyToOne(fetch = LAZY)
를 이용해 지연로딩으로 설정해주어야한다.
"xToMany"의 경우 기본값이 지연로딩이라 굳이 바꾸지 않아도된다.
CasCade를 사용하여 DataBase와의 편리함을 제공하자. CasCade를 사용하면 자동으로 하나의 객체만 persist해도 변경된 모든값이 저장된다.
@OneToOne(fetch = LAZY, cascade = CascadeType.ALL)
public void setMember(Member member){
this.member = member;
member.getOrders().add(this);
} //양방향 편의 메서드
public static void main(String[] args){
Member member = new Member();
Order order = new Order();
member.getOrders().add(order);
order.setMember(member);
} //일반적인 방법
이렇게 코드를 보면 양방향 편의 메서드를 활용했을 때, 더 짧은 코드길이를 확인할 수 있다. 평소대로라면 양쪽의 객체를 만들고 그 객체에 해당하는 값을 저장해야했지만, 양방향 편의 메서드를 이용해 설정하면된다.
setMember의 경우 연관관계 주인인 Order엔티티의 member를 설정해주는 메서드이다. 때문에 저렇게 사용할 수 있다.