본문 바로가기
Programming/JPA 프로그래밍

05. 연관관계 매핑 기초

by JKROH 2023. 10. 12.
반응형

연관관계 매핑

연관관계 매핑이란?

  • 대부분의 엔티티는 다른 엔티티와 연관 관계를 가진다.
  • 객체는 '참조'를 통해 연관관계를 맺는다. 예를 들어, 연관된 데이터를 조회할 때 Order객체는 필드의 Member객체를 참조(order.getMember())한다.
    • 이를 객체 그래프 탐색이라 한다.
  • 반면, 테이블은 '외래 키'를 통해 연관관계를 맺는다. 예를 들어, 연관된 데이터를 조회할 때 order 테이블은 member_id라는 외래 키를 통해 member테이블을 참조(SELECT * FROM ORDER O JOIN MEMBER M ON O.MEMBER_ID = M.ID)한다.
    • 이를 조인이라 한다.
  • 이렇게, 객체의 연관관계와 테이블의 연관관계는 차이를 보인다. 이 둘을 매핑하는 것을 연관관계 매핑이라 한다.
  • 연관관계 매핑을 이해하기 위한 핵심 키워드는 다음의 세 가지가 있다.
    • 방향 : 양방향 매핑과 단방향 매핑이 있다. 객체의 경우에는 둘 모두 존재하고, 테이블의 경우에는 양방향만 존재한다.
    • 다중성 : 연관관계는 일대일부터 일대다, 다대일, 다대다의 다중성이 있다.
    • 연관관계의 주인 : 객체를 양방향 연관관계로 만들면 연관관계의 주인을 정해야한다. 이후는 후술한다.

단방향 연관관계

  • 연관관계 중에선 다대일 단방향 관계를 가장 먼저 이해해야 한다. 다음의 관계를 통해 다대일 단방향 관계를 알아보자.
    • 회원과 팀이 있다.
    • 회원은 하나의 팀에만 소속될 수 있다.
    • 따라서, 회원과 팀은 다대일 관계다.

- 객체 연관관계

  • 회원 객체는 Member.team 필드로 팀 객체와 연관관계를 맺는다.
  • 회원 객체와 팀 객체는 단방향 관계다. 회원은 Member.team을 통해 팀을 알 수 있지만, 팀은 회원을 알 수 없다.

- 테이블 연관관계

  • 회원 테이블은 TEAM_ID 외래 키로 팀 테이블과 연관관계를 맺는다.
  • 회원 테이블과 팀 테이블은 양방향 관계다. 회원 테이블의 TEAM_ID 외래 키를 통해서 회원과 팀을 조인할 수도 있고, 팀과 회원을 조인할 수도 있다.

- 객체 연관관계와 테이블 연관관계의 가장 큰 차이

  • 참조를 통한 연관관계는 언제나 단방향이다. 객체 간 연관관계를 양방향으로 만들고 싶으면, 반대쪽에도 필드를 추가해 참조를 보완해야 한다.
  • 즉, 엄연히 이야기하면 객체간 양방향 연관관계는 서로 다른 두 개의 단방향 연관관계다.
  • 반면, 테이블은 하나의 외래 키로 양방향 조인이 가능하다. 즉, 외래 키를 통한 연관관계는 언제나 양방향이다.

연관관계 사용

- 저장

    public void save(){
        Member member = new Member(1, "회원 1");
        em.persist(member);
        
        Order order = new Order(1, "주문 1");
        order.setMember(member);
        em.persist(order);
    }

 

  • 주문 엔티티는 회원 엔티티를 참조하고 저장했다. JPA는 참조한 회원의 식별자를 외래 키로 사용해서 적절한 등록 쿼리를 생성한다.
  • JPA에서 엔티티를 저장할 때 연관된 모든 엔티티는 영속 상태여야 함을 명시하자.
    • 위에서는 회원 엔티티를 영속화하고 사용했다.

- 조회

  • 연관관계가 있는 엔티티는 참조를 통해 조회하는 방법(객체 그래프 탐색)과 JPQL을 이용하는 방법을 통해 조회 가능하다.
  • 객체 그래프 탐색
    • order.getMember(); 
  • JPQL
    • select o from Order o join o.member m where m.id = :id

- 수정

  • 저장과 같은 방법을 이용한다.
  • 값이 변경되면서 트랜잭션 커밋 시 플러시가 일어나면서 변경 감지 기능이 작동하며 UPDATE 쿼리가 실행된다.

- 삭제

  • 수정과 같은 방법을 이용하나, 수정자를 통해 연관객체를 null 처리한다.
  • 마찬가지로 변경 감지 기능이 작동하며 UPDATE쿼리가 실행된다. 외래 키 값에 NULL이 들어간다.

- 연관된 엔티티 삭제

  • 연관된 엔티티를 삭제하려면 기존에 있던 연관관계를 먼저 제거하고 삭제해야 한다. 그렇지 않으면 외래 키 제약 조건으로 인해 데이터베이스에서 오류가 발생한다.
  • 위의 예시에서는, 미리 order의 member를 null처리 해 연관관계를 삭제하고, member를 삭제해야 한다.

양방향 연관관계

  • 테이블은 기본적으로 양방향 연관관계이다. 따라서, 데이터베이스에는 단방향 연관관계에 별도로 추가할 필요는 없다.
  • 그러나 객체의 경우, 반대쪽에서도 알고 있어야하기 때문에, 반대쪽에도 참조 객체를 추가해주어야 한다.
  • 다시 말하지만, 객체에서의 양방향 연관관계는 단방향 두 개를 양방향처럼 사용할 뿐이다. 따라서, 단방향과 크게 차이는 없다.
  • 다만, 연관관계의 주인을 잘 정해야한다.

연관관계의 주인

  • 테이블은 외래 키 하나로 두 테이블의 연관관계를 관리한다.
  • 그러나 엔티티를 양방향으로 매핑하면 A -> B / B -> A 두 곳에서 서로를 참조한다. 결국, 객체의 연관관계를 관리하는 포인트가 두 곳으로 늘어난다. 여기서 둘의 차이가 발생한다.
  • 따라서, 두 객체 연관관계 중 하나를 정해서 테이블의 외래 키를 관리해야 한다.
  • 이를 연관관계의 주인이라 한다. 연관관계의 주인은 mappedBy 를 통해 지정한다.
  • 연관관계의 주인은 외래 키를 관리한다. 외래 키를 '관리'한다는 것은 외래 키를 '가지고있다'는 것이다. 즉, 외래 키가 있는 객체를 연관관계의 주인으로 지정해야 한다.
public class Order {
    
    @ManyToOne
    Member member;
}

//////////////////////////////////////////

public class Member {

    @OneToMany(mappedBy = "member") // 연관관계의 주인은 Order.member
    List<Order> orders = new ArrayList<>();
}
  • 쉽게 생각하면, 1 : N의 관계에서 1에 해당하는 객체가 연관관계의 주인이 된다. 만일 Order가 Member를 rebmem이라고 저장했으면 mappedBy = "rebmem"이 됐을 것이다.

양방향 연관관계 주의점

- 양방향 연관관계 저장

  • 기본적으로는 단방향 연관관계와 같다.
  • 위에서 사용했던 save()메서드를 그대로 사용해도 데이터베이스에는 두 데이터의 값이 잘 들어간다.

- 순수한 객체까지 고려한 양방향 연관관계

  • 그러나 객체 관점에서 양쪽 방향에 모두 값을 입력해주는 것이 안전하다.
  • 양쪽 방향 모두 값을 입력하지 않으면 JPA를 사용하지 않는 순수한 객체 상태에서 심각한 문제가 발생할 수 있다.
  • List에 값을 넣어주지 않으면, 당연히 List는 비어있기 마련이다.
  • 따라서, 객체의 양방향 연관관계는 양쪽 모두 관계를 맺어주자.

- 연관관계 편의 메서드

public class Order {

    @ManyToOne
    Member member;
    
    public void setMember(Member member){
        if(this.member != null){
            this.member.getOrders().remove(this);
        }
        this.member = member;
        member.getOrders().add(this);
    }
}
  • 위와 같이 Order#setMember 메서드를 만들어 놓으면 order.setMember(member); 를 통해 쉽게 양방향 설정을 할 수 있다.
  • 이 때 기존에 연관관계가 맺어져있었다면, 이를 반드시 먼저 삭제하고 새롭게 연관관계를 맺어주어야 한다.

정리

  • 단방향 매핑과 비교했을 때, 양방향 매핑은 복잡하다. 연관관계의 주인도 정해줘야 하고, 양방향 연관관계를 만들기 위해서는 로직도 꼼꼼히 신경써야한다.
  • 단방향에 비해 양방향이 지닌 장점은 객체 그래프 탐색 기능이 추가되어 탐색이 쉽다는 점 뿐이다.
  • 따라서, 일단 단방향 연관관계를 맺어놓고, 반대쪽에서도 탐색이 필요하다면 그 때 양방향으로 맺어주자.
반응형

'Programming > JPA 프로그래밍' 카테고리의 다른 글

07. 고급 매핑  (0) 2023.11.13
06. 다양한 연관관계 매핑  (1) 2023.11.06
03. 영속성 관리  (0) 2023.08.18
01. JPA 소개  (0) 2023.08.15
0장. 들어가며  (0) 2023.08.07

댓글