스프링 공부/JPA

[JPA] 10. 연관관계 매핑의 방향성, 그리고 주인

장아장 2022. 11. 2. 15:16

테이블에서는 외래키 하나로 다른 테이블을 자유롭게 이동할 수 있다.

하지만, 테이블은 이게 아닌, 참조한 객체를 매핑해서 외래키처럼 데이터베이스에 적용시킨다. 

 

한 객체가 다른 객체를 참조하고, 이를 @JoinColumn으로 설정한 상태를 단방향 매핑이라고 한다. 

참조를 하는 객체는 참조된 객체를 탐색할 수 있다. 하지만, 참조된 객체는 참조한 객체에 대한 정보가 없기 때문에 탐색을 실행할 수 없다

 

이럴 때 참조된 객체도 참조한 객체를 탐색하기 위해서 양방향 매핑이라는 것을 한다. 

public class Order {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "ORDERS_ID")
    private Long id;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "MEMBER_ID")
    @OnDelete(action = OnDeleteAction.CASCADE)
    private Member member;

이렇게 주문 객체에 멤버가 참조되어있다. 멤버에서 주문 객체를 탐색하기 위해서 양방향 매핑을 하려면, 

@OneToMany(mappedBy = "member")
@Column(name = "ORDER_ID")
private List<Order> myOrder = new ArrayList<>();

이런 방식으로 mappedBy를 통한 양방향 매핑을 해주어야 한다. 

이는 데이터베이스에는 적용되지 않지만, 읽기 전용으로 멤버 객체를 불러올 때, 주문 객체들도 읽기전용으로 가져오는 것이다. 

 

생각해보면, 단방향 매핑을 두개 만드는 것과 같다. 

외래키가 있는 쪽에서 없는쪽으로 단방향 하나, 외래키가 없는 쪽에서 외래키가 있는 쪽으로 단방향 하나

이런 식으로 만들어주면 양방향 탐색이 가능해진다. 

 

이렇게 보면, 양쪽 객체 모두 외래키를 가지고 있는 것 처럼 보이지만, 실제로 그렇지 않다. 

책이나, 여러 강의에서 하는 말은 '연관관계의 주인이 외래키를 가지고 있는다.' 라고 한다. 

 

솔직히, 이 말이 와닿지 않았다. 데이터베이스를 다시 생각해보며 어떤게 이상적일지 생각해보고 있었다. 

후에 정리해두겠지만, 위의 예시는 다대일 연관관계이다. 

데이터베이스 테이블에서 한 칸(데이터)는 하나의 데이터만을 담는게 이상적이다.

데이터를 열거하지 않고, 한 정보만을 담는게 중점이다. 

그런데, private List<Order> myOrder = new ArrayList<>();을 하나의 칸에 담는다?

리스트의 데이터들을 열거해서 담는 것이다. 어림없지 암!!

다대일에선 다 쪽이 외래키를 가진다. 여러 주문이 하나의 멤버를 외래키로 갖고있는 것이다. 

 

그렇다면, 다대일이나 다대다가 아니라 일대일이라면(설마 일대다를 생각하진 않겠지) 어떨까?

이는 일대일 매핑때 정리해야할 것 같다. 너무 멀리왔다. 

 

가장 중요한 것은, 연관관계의 주인을 정해야 하며, 이 주인은 @JoinColumn을 가져야 한다는 것이다. 

주인이 아닌 객체는 mappedBy를 달아 외래키로 참조된 객체임을 인식시켜주고, 읽기 전용으로 데이터를 받아오게 해준다는 것이다. 

 

그런데, 양방향 매핑에서 주의해야 할 점이 있다. 

  • 연관관계의 주인에 참조 객체를 입력하지 않는다. 그러면 외래키가 null로 저장되며, 탐색을 할 수 없게 된다. 
  • 연관관계의 주인의 외래키를 입력, persist후에 commit하지 않는다면, 참조된 객체에서 주인객체를 불러와도 영속화 시킨 객체는 나오지 않는다. 1차 캐시에 객체가 저장되어있지만, 참조된 객체에서 참조한 객체들을 불러올 때, 데이터베이스에서 불러오기 때문에 쓰기지연 sql저장소의 객체들은 나오지 않는다. 
  • 그러니, 기본적으로 객체를 새로 만들어 영속화시킬 때, 참조된 객체에 해당 객체를 넣어준다. 
  • 이를 기능화시켜 한번에 해주면 된다.(메서드로 만들어 쓰라는 뜻!)
  • 이 기능이 서로 서로 계속 불러오게 해서 무한루프에 빠지지 않게 조심하자. 

이러한 주의점들이 존재한다. 그리고 이를 모든 연관관계에 적용시키면 머리만 아프다. 

그래서 추천시되는 방법은, 

  1. 일단 단방향으로 설계를 완료한다. 
  2. 양방향은 역방향 조회 기능이 추가된
  3. 양방향은 생각하고 만들어야 거리가 늘어난다. 설계에서 양방향 매핑이 들어가는 것이 아닌, 참조 조회의 과정에서 역방향 탐색이 필요할 추가되는 것이 나을 수도 있다. 

이러한 순서를 가지고 만들면, 꼭 필요한지 생각하고, 필요한 순간에만 양방향 매핑이 가능하게 해주면 된다.