[Spring boot] JPA 어노테이션(2)

90000e·2023년 11월 9일
0

[Spring]

목록 보기
6/8

ManyToMany

  • 보통 ManyToMany(다대다) 관계는 실무에서 사용하지 않는다고한다. 다대다 관계를 표현하기 위해서는 중간 테이블을 만들어 일대다, 다대일로 연결해주어야한다고한다.

이렇게 카테고리와 아이템을 연결할때 서로 다대다 연결이라고한다면, 중간 테이블을 만들어 연결한다고한다. 저번 포스팅에서 알 수 있듯이 다대일 과 일대다 관계는 양방향 연결일 때, 어디에 맵핑되는지 설정을 해주어야 한다. 분명 '다'인 외래키에 연관관계 주인역할을 선언해주어야한다고 했는데, 다대다 관계는 양쪽이 '다'관계이다.

다대다 관계일때 연관관계 주인을 직접 설정해 줘야하는데, 이때 몇가지 규칙이 존재한다.

  • 외래 키의 관리 : 데이터베이스 테이블에서 항상 외래키가 필요하다. 연관관계 주인을 선택하면 해당 엔티티가 외래 키를 관리하고 데이터베이스에 영향을 미치게된다.
  • 편의성 : 연관관계 주인을 선택할 때 어떤 쪽이 논리적으로 더 많은 편의성을 제공하는지 고려해야한다.
  • 일관성 : 코드베이스에서는 하나의 엔티티에 대한 연관관계 주인을 일관적으로 유지해야한다.

이 세가지 규칙을 이용해 연관관계 주인을 잘 설정해야한다.

@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를 사용하지 말자

Setter가 모두 열려있다면, 변경포인트가 너무 많아 유지보수가 어렵다. 그래서 오로지 Gatter만 사용해야하며, 값의 수정보다는 새로운 객체를 생성해 값을 추가해야한다.

생성자는 public or protected로 설정!

JPA 구현 라이브러리가 객체를 생성할 때, 리플렉션 같은 기술을 사용할 수 있어야 하기 때문에 이렇게 설정해주어야한다.

모든 연관관계는 지연로딩으로 설정!

즉시로딩은 최악의 경우 하나 가져올때 모든 연관관계를 다 끌고오게된다 그래서 지연로딩인 Lazy로 설정해주어야한다. 즉시로딩은 실무에서 절대 사용하면 안된다.

@ManyToOne과 같이 "xToOne"의 형태는 즉시로딩이 기본값이다. 때문에 항상 @ManyToOne(fetch = LAZY)를 이용해 지연로딩으로 설정해주어야한다.

"xToMany"의 경우 기본값이 지연로딩이라 굳이 바꾸지 않아도된다.

Cascade를 적극 활용하자!

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를 설정해주는 메서드이다. 때문에 저렇게 사용할 수 있다.

profile
내가 복습하려고 쓰는 블로그

0개의 댓글