Dev/Spring

연관관계 매핑(양방향)

oxdjww 2023. 11. 21. 01:49
728x90
반응형

✅ 양방향 매핑이란?

  • 객체의 연관관계 중 하나인 양방향 매핑은, 사실상 단방향 매핑이 두번 이루어진 것이다.
  • 즉, 개념적으로 두 개의 단방향 매핑을 추상적으로 양방향 매핑이라 칭하는 것이다.

✅ 양방향 연관관계 매핑의 필요성

테이블과 객체를 비교해보자.

  • 테이블에서는 외래 키 하나로 두 테이블의 연관관계를 확인할 수 있다.

    • 외래 키를 가지고 조인하면 두 테이블간 데이터의 결합과 접근성이 자유롭다.
    • 즉, 외래 키 하나만으로 한 컬럼의 연관된 데이터를 획득할 수 있다.
  • 하지만 객체에서는?

    • 객체를 탐방하려면 참조가 쌍방으로 존재해야 한다.
    • 참조가 양방향으로 존재해야 참조와 역참조가 가능한 구조라는 것이다.
  • 이런 패러다임의 차이를 극복하기 위해 양방향 매핑을 한다.

  • 하지만, 엔티티 연관관계를 설정할 때 우선 단방향 매핑으로 구성하되, 비즈니스 로직상 역참조가 필요할 경우에 양방향 매핑을 해야 한다.

  • 불필요한 양방향 매핑은, 로직의 복잡성을 증가시키고 문제 발생 시 원인을 찾기 어렵게 한다.

🌱 구체적으로 어떤 경우?

예를 들어..

  • OrderOrderItem이 존재할 때, 이는 1:N 관계이므로 외래키가 OrderItem에 존재한다.
  • 하지만, 일반적으로 주문(Order)에서 주문한 상품의 목록(OrderItem 여러개)을 조회하는 일이 잦기에 이를 List<OrderItem>로 구현하고자 한다.
  • 위는 단순한 예시일 뿐이고, 우리는 단방향 매핑에서 사용했던 예제인 MemberTeam의 경우를 이어서 학습한다.

Member & Team

🌱 엔티티 매핑

  • Member는 동일하다.

          @Entity
    
      public class Member {
          @Id @GeneratedValue
          private Long id;
    
          @Column(name = "USERNAME")
          private String name;
          private int age;
    
          // @Column(name = "TEAM_ID")
          // private Long teamId;
    
          @ManyToOne
          @JoinColumn(name = "TEAM_ID")
          private Team team;
          ...
      }
  • Team에는, Member 여러개를 관리하는 컬렉션 List를 추가한다.

      @Entity
      public class Team {
          @Id @GeneratedValue
          private Long id;
    
          private String name;
    
          @OneToMany(mappedBy = "team")
          List<Member> members = new ArrayList<member>();
          ...
      }

🌱 엔티티 다루기

  • 이를 통해 역방향으로 객체 그래프를 탐색할 수 있는 것이다.

      // 조회
      Team findteam = em.find(Team.class, team.getId());
    
      int memberSize = findTeam.getMembers().size(); // 역방향 조회

✅ 연관관계의 주인

  • mappedBy= owner 의미 : “내 주인은 owner야”
  • 연관관계의 주인이라는 개념이 양방향 매핑에서는 필요하게 된다.

  • 외래 키를 관리해줄 주체가 필요해진 것이다.
  • Member 레코드에서, TEAM_ID의 변경 기준은 무엇일까?
    • Member 객체의 Team team이 변경되었을 때?
    • Team 객체의 List members 이 변경되었을 때?
  • 이 기준이 바로 주인(Owner)이다.
    • 객체의 두 관계 중 하나를 연관관계의 주인으로 설정하면 된다.

🌱 양방향 매핑 규칙

  • 연관관계의 주인만이 외래 키를 관리한다. (등록, 수정)
  • 주인이 아닌 쪽은 읽기만 가능
  • 주인은 mappedBy 사용 안함
  • 주인이 아니면 mappedBy 로 주인 지정

🌱 연관관계의 주인 기준

연관관계의 주인은 외래키쪽으로

  • 위의 말을 거의 공식처럼 외우는 경우가 많다.
    • 이해를 하고 외워보자.
  • 외래키가 있는 쪽은, 1:N에서 N쪽이다. 역의 경우에서 모순을 찾아 증명해보자.

💎 외래키가 없는, 1:N에서 1쪽이 주인이라면..

  • 1쪽이 주인이면, Team에 외래 키가 존재하고, 멤버 여러명이 한 Team에 속하는 구조일 것이다.
  • Team에 외래 키가 존재한다는 것은, MemberPKmemberId를 갖는 레코드가 다수 있다는 뜻이 된다.
  • 그 뜻은 member의 레코드 중 같은 팀인 memberTeamPK값이 같을 것이고,
  • 그럼 유일해야 하는 PK의 값에 중복이 발생한다.

✅ 양방향 매핑 시 하는 실수

🌱 연관관계의 주인에 값을 입력하지 않는 경우

Team team = new Team();
team.setName("TeamA");
em.persist(team);

Member member = new Member();
member.setName("member1");

//역방향(주인이 아닌 방향)만 연관관계 설정
team.getMembers().add(member);

em.persist(member);
  • 연관관계 주인이 아닌 쪽의 엔티티에 접근하여 .add()를 한다.
  • 그렇게 되면 실제로 삽입된 member 레코드의 TEAM_ID 필드 값은 null로 지정되게 된다.

그렇기에 연관관계의 주인에 값을 설정해야 한다.

Team team = new Team();
team.setName("TeamA");
em.persist(team);

Member member = new Member();
member.setName("member1");

team.getMembers().add(member); //연관관계의 주인에 값 설정
member.setTeam(team); //**

em.persist(member);
  • member.setTeam(team); 을 통해, 연관관계 주인의 컬럼에 값을 더해준 것을 볼 수 있다.
  • 이 코드가 있기 때문에, team.getMembers().add(member); 코드는 사실상 주석처리를 해줄 수 있다.
  • 나중에 데이터베이스에 등록된 team으로 team.getMembers()를 사용할 때, 지연로딩에 의해 이 컬럼(members)과 연관된 값들에 대해 SELECT SQL을 한번 날리게 된다.
  • 그 때 members에서 TEAM_ID가 같은 값들을 조회하며 갱신되는 것이다.

🌱 연관관계 편의 메서드

  • 순수 객체 상태를 고려, 혹은 객체지향적인 특징에 잘 맞추기 위해 항상 양쪽에 값을 설정하자.

  • 즉, 위의 예제를 빌어 말을 보충하자면

    • member.setTeam(team);
    • team.getMembers().add(member);
    • 이 두 가지를 모두 해주자는 것이다!
  • 하지만 두 가지 작업을 모두 해주는 것은 사람이 실수를 할 수도 있기에, 메서드로 따로 구현하여 빼먹는 일이 없도록 하면 더 좋다.

      // 그 것을 연관관계 편의 메서드라 하며, 두 객체 중 한 쪽에만 구현하는 것이 관례이다.
      // team 내부의 함수. 멤버 변수로 List<member> members 존재.
      public void addMember(Member member) {
          members.add(member);
          member.setTeam(this);
      }

Ref

김영한 강사님, JPA 프로그래밍 - 기본편


감사합니다.

728x90
반응형

'Dev > Spring' 카테고리의 다른 글

연관관계 매핑(단방향)  (0) 2023.11.21
변경 감지(Dirty Checking)  (0) 2023.11.20
JPA를 왜 쓸까?  (0) 2023.11.20
영속성 컨텍스트란  (0) 2023.11.20
준영속과 변경감지  (0) 2023.10.25