Blog
스터디
CS Study with SON
24주차
JPA 연관관계 맵핑

출처 - https://github.com/jmxx219/CS-Study (opens in a new tab)

JPA 연관관계 맵핑


연관관계 매핑 기초

◻ 연관관계가 필요한 이유

객체지향 설계의 목표는 자율적인 객체들의 협력 공동체를 만드는 것이다.
by 조영호(객체지향의 사실과 오해)

  • 테이블 중심 설계의 문제점

    • 객체를 테이블에 맞추어 데이터 중심으로 모델링하면, 협력 관계를 만들 수 없음

      • 테이블은 외래키로 조인을 사용해서 연관된 테이블을 찾지만, 객체는 참조를 사용해서 연관된 객체를 찾음
    • 따라서 연관관계가 없는 객체라면 참조 대신에 외래키를 그대로 사용하기 때문에 테이블과 객체 사이에는 큰 간격이 존재함

  • 연관관계가 없는 객체 ➜ 참조 대신에 외래키를 그대로 사용함

    @Entity
    public class Member {
        @Id @GeneratedValue
        private Long id;
        
        @Column(name = "USERNAME")
        private String name;
        
        @Column(name = "TEAM_ID")
        private Long teamId;
    }
     
    @Entity
    public class Team {
        @Id @GeneratedValue
        private Long id;
        private String name;
    }
    Team team = new Team();
    team.setName("TeamA");
    em.persist(team);
     
    Member member = new Member();
    member.setUserName("member1");;
    member.setTeamId(team.getId());
    em.persist(member);
     
    Member findMember = em.find(Member.class, member.getId());
    Long teamId = findMember.getTeamId(); // 연관관계가 없음
    Team findTeam = em.find(Team.class, teamId);
    • 외래키 식별자를 직접 다뤄서 저장함
    • 식별자로 다시 조회하는데, 이는 객체 지향적인 방법이 아님
      • member를 조회했음에도 불구하고 member가 속한 team을 알기 위해서 teamId(FK)로 다시 조회해서 가져와야 함
      • 연관관계가 없어 객체지향스럽지 않음

◻ 단방향 연관관계

객체 지향 모델링

  • 객체 연관관계 사용
    • Team의 Id가 아닌 Team 참조 값을 그대로 가져옴
    • Member 객체의 Team 레퍼런스와 Member 테이블의 Team_Id(FK)를 매핑함
  • 객체의 참조와 테이블의 외래키를 매핑
    @Entity
    public class Member {
        @Id @GeneratedValue
        private Long id;
          
    //    @Column(name = "TEAM_ID")
    //    private Long teamId;
          
        @ManyToOne
        @JoinColumn(name = "TEAM_ID")
        private Team team;
    }
    • 연관관계가 무엇인지(다대일), 이 관계를 정의할 때 조인하는 컬럼은 무엇인지 나타냄
  • ORM 매핑
  • 연관관계 저장
    // member.setTeamId(team.getId());
    member.setTeam(team); 
    • member에 team을 참조해서 저장하면, JPA가 DB에 엔티티를 저장할 때 알아서 team 엔티티에서 PK 값을 꺼내 member 테이블의 FK 컬럼에 저장함
  • 참조로 연관관계 조회 - 객체 그래프 탐색
    Member findMember = em.find(Member.class, member.getId());
    Team findTeam = member.getTeam(); // 참조 사용해서 연관관계 조회
  • 연관관계 수정
    Team teamB = new Team(); //새로운 팀 B
    teamB.setName("TeamB");
    em.persist(teamB);
      
    member.setTeam(teamB); //회원에 새로운 팀B 설정
    • 더티 체킹을 이용한 연관관계 수정

◻ 양방향 연관관계

  • 테이블의 연관관계에는 외래키 하나에 양방향이 다 존재함
    • 테이블은 외래키로 조인해서 연관 관계를 만들기 때문
      • Member에서 자신이 속한 팀을 알고 싶으면 Member의 TEAM_ID(FK)와 TEAM의 TEAM_ID(PK)를 조인하면 됨
      • Team에서 속해있는 멤버들을 알고 싶으면 TEAM의 TEAM_ID(PK)와 Member의 TEAM_ID(FK)를 조인하면 됨
    • 사실 방향이란 개념 자체가 없음
  • 객체는 참조를 통해 연관관계를 설정하기 때문에, Team에서 Member에서 서로에 대한 참조를 모두 가지고 있어야 함
  • 엔티티
    • Member 엔티티는 단방향과 동일함
    • Team 엔티티에만 member 컬렉션을 추가
      @Entity
      public class Team {
          ...
          @OneToMany(mappedBy = "team") 
          private List<Member> members = new ArrayList<Member>(); // 추가
      }
    • 반대 방향으로 객체 그래프 탐색 가능
      Member findMember = em.find(Member.class, member.getId());
      List<Member> members = findMember.getTeam().getMembers(); // 역방향 조회

양방향으로 매핑하면 신경써야할 점이 많기 때문에 가급적이면 단방향이 좋음


◻ 연관관계의 주인과 mappedBy

  • mappedBy는 JPA에서 이해하기 가장 어려운 부분임
  • 객체와 테이블간에 연관관계를 맺는 차이를 이해해야 함!

객체와 테이블이 관계를 맺는 차이

  • 객체의 양방향 관계
    • 객체 연관관계 = 2개
      • 회원 ➜ 팀 연관관계 1개 (단방향)
      • 팀 ➜ 회원 연관관계 1개 (단방향)
    • 객체의 양방향 관계는 사실 양방향 관계가 아닌 서로 다른 단방향 관계 2개
      • 객체 세상에서는 양방향 연관관계를 맺으려면, 참조가 양쪽 객체에 있어야 함
  • 테이블의 양방향 연관관계
    • 테이블 연관관계 = 1개
      • 회원 ↔ 팀 연관관계 1개 (양방향, 사실 방향이 없는 것)
    • 테이블은 외래 키 하나도 두 테이블의 연관관계를 관리함
      • 외래키 값 하나로 테이블을 조인하면 양방향 연관관계를 가짐

둘 중 하나로 외래키를 관리해야 함

  • 두 가지 참조 값이 존재
    • Member ➜ Team 참조값
    • Team ➜ Member 참조값
  • 둘 중 어떤 것으로 테이블의 외래키에 매핑해야 하는지에 대한 고민
    • Member 객체의 team 값을 변경했을 때, 외래키 값이 업데이트 되어야 하는지
    • Team 객체의 members 값을 변경했을 때, 외래키 값이 업데이트 되어야 하는지
  • DB 입장에서는 TEAM_ID(FK)만 업데이트되면 되기 때문에 둘 중 하나로 외래키를 관리해야 함!
    • 둘 중 어떤 것(Team이나 Member)으로 관리를 해야할 지 주인을 정해야 함(이것이 연관관계의 주인)

연관관계의 주인(Owner)

  • 양방향 매핑 규칙

    • 객체의 두 관계 중, 하나를 연관관계의 주인으로 지정
    • 연관관계의 주인만이 외래키를 관리(등록, 수정)
    • 주인이 아닌 쪽은 읽기만 가능
    • 주인은 mappedBy 속성 사용 x
    • 주인이 아니면 mappedBy 속성으로 주인 지정
  • 그래서 누구를 주인으로?

    • 외래 키가 있는 곳을 주인으로❗❗
      • ManyToOneMany 쪽이 연관관계의 주인으로 하면 됨
      • 여기서는 Member.team이 연관관계의 주인



  • 외래키가 있는 쪽의 테이블과 대응되는 엔티티에 있는 참조를 연관관계의 주인으로 정하는 것이 헷갈리지 않음

    • 만약 외래키가 있는 곳이 아닌 Team.members를 연관관계의 주인으로 정할 경우
    • Team에 있는 members를 수정했는데 Update 쿼리는 다른 테이블(Member)에 날아감(헷갈림)
  • 외래키가 존재하는 엔티티에서 관리를 나중에 문제가 발생하지 않음

    • DB 입장에서는 ManyToOne(다대일) 관계에서 외래키가 있는 쪽이 Many(다)고 외래키가 없는 쪽이 One(일)
      • DB의 Many쪽이 무조건 연관관계의 주인이 됨
    • 객체에서는 @ManyToOne, @XXXToOne 쪽이 무조건 연관관계의 주인이 됨
    • 연관관계의 주인은 비즈니스적으로 큰 의미가 없음
  • 외래키가 있는 쪽이 연관관계 주인이 되어야 설계가 깔끔해짐


양방향 매핑시 가장 많이 하는 실수

  • 연관관계의 주인에 값을 입력하지 않음

    Team team = new Team );
    team.setName("TeamA");
    em.persist(team);
      
    Member member = new Member();
    member.setName("member1");
      
    team.getMembers().add(member); //역방향(주인이 아닌 방향)만 연관관계 설정
      
    em.persist(member);
    • 연관관계의 주인만이 외래키 값을 등록, 수정할 수 있음
      • 연관관계의 주인이 아닌쪽은 조회만 가능함
    • JPA에서 update나 insert할 때는 mappedBy된 쪽은 보지 않음
      • mappedBy는 그냥 읽기 전용, 가짜 매핑이기 때문에 실제로 DB에 반영되지 않음
  • 양방향 매핑시 연관관계의 주인에만 값을 입력(양쪽에 다 값을 입력하지 않는 경우)

    Team team = new Team();
    team.setName("TeamA");
    em.persist(team);
      
    Member member = new Member();
    member.setName("member1");
    member.setTeam(team); //연관관계의 주인에 값 설정
     
    em.persist(member);
    • 연관관계의 주인에만 값을 설정해주면 영속성 컨텍스트에 있는 team 객체의 member 컬렉션은 여전히 비어있는 상태
      • 따라서 트랜잭션 안에서 영속성 컨텍스트에 flush하고 clear 되기 전에 해당 컬렉션을 조회하면 정상적인인 결과가 출력되지 않음
      • 물론, 커밋 시점에는 연관관계의 주인에 의해서 DB에 업데이터 쿼리가 날아감
    • 테스트 케이스 작성 시, JPA 없이도 동작하게 순수 자바 코드로 작성하기 때문에 Team.getMembers()를 하면 값이 없어 null로 나오는 문제가 발생
  • 결론

    • 순수 객체 상태를 고려해서 항상 양쪽에 값을 설정하자
    • 연관관계 편의 메서드를 생성하자
      @Entity
      public class Member {
          @ManyToOne
          @JoinColumn(name = "TEAM_ID")
          private Team team;
       
          public Team getTeam() {
              return team;
          }
       
          public void changeTeam(Team team) {
              this.team = team;
              team.getMembers().add(this);
          }
      }
      • 진짜 주인에 값을 넣을 때, 자동으로 가짜 주인에도 넣을 수 있도록 편의 메소드를 활용
        • 연관관계 편의 메서드나 JPA 상태를 변경하는 건 setter를 활용하지 않음
      • 이는 단순하게 getter, setter 관례에 의한게 아니라 어떤 작업을 수행하는지 명확하게 알 수 있어 좋음
    • 양방향 매핑 시, 무한루프를 조심하자
      • Lombok의 toString()은 사용하지 말기
      • JSON 생성 라이브러리와 관련해서 컨트롤러에서 entity를 절대 반환하지 않기
        • entity를 반환할 경우 문제가 생김
          • 무한루프 발생
          • 나중에 entity를 변경하는 순간 API 스펙이 바뀌어 버림
        • DTO을 변환해서 반환하는 것이 좋음

◻ 정리

  • JPA 사용 시, 엔티티 설계할 때
    • 단방향 매핑으로 모두 끝내기
    • 일대다일 때 Many(다)쪽에 연관관계 매핑을 전부 설정해주고 설계를 끝내기
    • 실제 애플리케이션을 개발하는 단계에서 양방향 매핑을 고려
      • 객체 입장에서 양방향 매핑이 크게 이득되는 것이 없음
  • 양방향 매핑
    • 단방향 매핑만으로도 이미 연관관계 매핑은 완료
      • JPA 모델링할 때 단방향 매핑으로 처음 설계를 끝내야 함(양방향 매핑을 하면 안됨)
        • 실무에서는 사실 객체만으로 설계할 수 없고, 테이블 설계를 먼저 그리면서 객체 설계를 같이 들어가야 함
        • 그 시점에 테이블 관계에서 대략전인 FK가 나옴
        • 결국 Many 쪽에서 단방향 매핑(ManyToOne, OneToOne)을 다 걸어서 들어가야하기 때문에 이때 양방향 매핑을 하지 말자
    • 양방향 매핑은 반대 방향으로 조회(객체 그래프 탐색) 기능이 추가된 것 뿐임
    • JPQL에서 역방향으로 탐색할 일이 많음
    • 단방향 매핑을 잘하고 양방향은 필요할 때 추가해도 됨(테이블에 영향을 주지 않음)
      • 처음에 무조건 단방향 매핑으로 설계를 끝내고, 그리고 나서 역방향으로 조회 기능이 필요할 때 양방향을 사용하기
  • 연관관계의 주인을 정하는 기준
    • 비즈니스 로직을 기준으로 연관관계의 주인을 선택하면 안됨
    • 연관관계의 주인은 외래 키의 위치를 기준으로 정해야함

다양한 연관관계 매핑

◻ 연관관계 매핑 시 고려사항

  • 다중성
    • 다대일: @ManyToOne
    • 일대다: @OneToMany
    • 일대일: @OneToOne
    • 다대다: @ManyToMany (실무에서 사용 x)
  • 단방향, 양방향
    • 테이블
      • 외래키 하나로 양쪽 조인 가능
      • 사실 방향이라는 개념 존재 x
    • 객체
      • 참조용 필드가 있는 쪽으로만 참조 가능
      • 한쪽만 참조하면 단방향
      • 양쪽이 서로 참조하면 양방향(사실은 단방향이 2개)
  • 연관관계의 주인
    • 테이블은 외래키 하나로 두 테이블이 연관관계를 맺음
    • 객체 양방향 관계는 참조가 2군데 존재. 둘 중 테이블의 외래키를 관리할 곳을 지정해야 함
    • 연관관계의 주인: 외래키를 관리하는 참조(등록, 수정)
    • 주인의 반대편: 외래키에 영향을 주지 않음, 단순 조회만 가능

◻ 다대일

다대일 단방향 [N:1]

  • 가장 많이 사용하는 연관관계

  • 다대일의 반대는 일대다 관계

  • 관계형 DB에서는 설계 상, 항상 다(N)쪽에 외래키가 있음




    • 회원과 팀은 다대일 연관관계
      • 회원은 Member.team으로 team 엔티티를 참조할 수 있지만, 반대로 팀에는 회원을 참조하는 필드가 없음
    • @JoinColumn(name = "TEAM_ID")
      • Member.team 필드를 TEAM_ID 외래키와 매핑하여 외래키를 관리함

다대일 양방향 [N:1, 1:N]

  • 외래키가 있는 쪽이 연관관계의 주인
    • 일대다와 다대일 관계는 항상 다(N)에 외래키가 있음
  • 양방향 연관관계는 항상 서로를 참조해야 함
    • 어느 한 쪽만 참조하면 양방향 연관관계가 성립하지 않음

    • 항상 서로 참조하게 하려면 연관관계 편의 메서드를 작성하는 것이 좋음

    • 연관관계 편의 메서드는 한 곳에 작성하거나 양쪽 모두 작성할 수 있는데, 양쪽에 다 작성하면 무한루프에 빠지기 때문에 주의해야 함




      • 다쪽인 MEMBER 테이블이 외래키를 가지고 있기 때문에, Member.team이 연관관계의 주인임
      • 실선이 연관관계의 주인(Member.team)이고, 점선(Team.members)는 연관관계의 주인이 아님

◻ 일대다

일대다 단방향 [1:N]

  • 일대다(1:N)에서 일(1)이 연관관계의 주인

  • 테이블 일대다 관계는 항상 다(N)쪽에 외래키가 있음

  • 객체와 테이블의 차이 때문에 반대편 테이블의 외래키를 관리하는 특이한 구조(권장 x)

  • @JoinColumn을 꼭 사용해야 함

    • 그렇지 않으면 조인 테이블 전략(중간에 테이블을 하나 추가하여 연관관계를 관리함)을 기본으로 사용해서 매핑함



    • Team은 Member를 알고 싶은데, Member 입장에서는 Team을 알고싶지 않은 경우
      • 객체 입장에서 이런 설계가 나올 확률이 높음
    • DB 설계 상, 일(1)은 외래키가 들어갈 수 없고 무조건 다(N)쪽에 외래키가 들어가야 함
      • 하지만 다(N)쪽인 Member 엔티티에는 외래키를 매핑할 수 있는 참조 필드가 없음
      • 따라서 Team.members에서 회원 테이블의 TEAM_ID(FK) 외래키를 관리함
        • 보통 자신이 매핑한 테이블의 외래키를 관리하지만, 이 매핑은 반대쪽 테이블에 있는 외래키를 관리함
    • Team.members의 값을 바꿨을 때, Member 테이블을 있는 TEAM_ID(FK)를 업데이트해주어야 함
  • 단점

    • 엔티티가 관리하는 외래키가 다른 테이블에 존재함
      • 성능 문제도 있지만 관리가 부담스러움
    • 다른 테이블에 외래키가 있으면, 연관관계 관리를 위해 추가로 UPDATE SQL을 실행해야 함
      • 만약 본인 테이블에 외래키가 있으면, 엔티티의 저장과 연관관계 처리를 INSERT SQL 한 번으로 처리 가능

일대다 단방향 매핑보다는 다대일 양방향 매핑을 사용하자!
다대일 양방향 매핑은 관리해야 하는 외래키가 본인 테이블에 존재하기 때문에 일대다 단방향 매핑 같은 문제가 발생하지 않음


일대다 양방향 [1:N, N:1]

  • 해당 매핑은 공식적으로 존재하지 않음
    • 대신 다대일 양방향 매핑을 사용해야 함
    • 일대다 양방향과 다대일 양방향은 같은 말이지만, 왼쪽을 연관관계의 주인으로 가정해서 분류함
    • 양방향 매핑에서 @OneToMany는 연관관계의 주인이 될 수 없음
      • 관계형 DB의 특성상 일대다와 다대일 관계에서 항상 다(N)쪽에 외래키가 존재함
    • 일대다 양방향 매핑이 완전히 불가능한 것은 아님
  • 읽기 전용 필드를 사용해서 양뱡향처럼 사용하는 방법
    • @JoinColumn(insertable=false, updatable=false)
      @Entity
      public class Member {
          @Id
          @GeneratedValue
          @Column(name = "MEMBER_ID")
          private Long id;
          
          @ManyToOne
          @JoinColumn(name = "TEAM_ID", insertable = false, updatable = false) // 읽기 전용이 됨
          private Team team;
      }
    • 일대다 단방향 매핑 반대편에 같은 외래키를 사용하는 다대일 단방향 매핑을 읽기 전용으로 하나 추가함
    • 둘 다 같은 키를 관리하므로 문제가 발생할 수 있기 때문에 다대일 쪽은 insetableupdatable 설정으로 읽기만 가능하게 함
  • 결론은 다대일 양방향을 사용하자
    • 해당 방법은 일대다 양방향 매핑이라기 보다 일대다 단방향 매핑 반대편에 다대일 단방향 매핑을 읽기 전용으로 추가해서 양방향처럼 보이도록 하는 방법

    • 따라서 일대다 단방향 매핑이 가지는 단점을 그대로 가지게 됨




◻ 일대일

일대인 관계 [1:1]

  • 일대일 관계는 그 반대도 일대일
  • 주 테이블과 대상 테이블 중에 누가 외래키를 가질 지 선택해야 함
    • 주 테이블에 외래키
      • 주 객체가 대상 객체의 참조를 가지는 것처럼 주 테이블에 외래키를 두고 대상 테이블을 참고함
      • 객체지향 개발자가 선호하며 JPA 매핑에 편리함
      • 장점: 주 테이블이 외래키를 가지고 있기 때문에, 주 테이블만 조회해도 대상 테이블에 데이터가 있는지 확인 가능
      • 단점: 값이 없으면 외래키에 null 값을 허용함
    • 대상 테이블에 외래키
      • 대상 테이블에 외래키가 존재함
      • 전통적인 데이터베이스 개발자가 선호함
      • 장점: 주 테이블과 대상 테이블을 일대일에서 일대다로 관계를 변경할 때 테이블 구조를 그대로 유지할 수 있음
      • 단점: 무조건 양방향으로 만들어야 하며, 프록시 기능의 한계로 지연로딩으로 설정해도 항상 즉시 로딩됨
  • 외래 키에 데이터베이스 유니크(UNI) 제약조건 추가

일대일: 주 테이블에 외래키

  • 단방향

    • 다대일(@ManyToOne) 단방향 매핑과 유사



    • Member를 주테이블이라고 생각(둘 중 어디에 넣어도 됨)
    • DBA 입장에서와 개발자 입장에서의 딜레마 발생
      • DBA 입장
        • 나중에 하나의 Member가 여러 개의 Locker를 가질 수 있다고 할 때, Locker에 두는게 여러 개를 insert할 수 있어 편리함
        • 만약 Member에 외래키가 있으면 코드나 기능을 많이 변경해야 함
      • 개발자 입장
        • MemberLocker가 있는 것이 성능도 그렇고 여러가지로 유리함
        • Member는 거의 필수적으로 조회되는데 조인없이 DB 쿼리 한 방으로 Member를 가져오면 Locker 데이터가 있는지 없는지 판단하기 쉬움
  • 양방향

    • 다대일 양방향 매핑처럼 외래키가 있는 곳이 연관관계의 주인
    • 반대편읜 mappedBy 적용



    • 반대편 LockerMember 필드를 추가함
    • MEMBER 테이블이 외래키를 가지고 있으므로, Member.locker가 연관관계의 주인
    • 반대 매핑인 Locker.member는 mappedBy를 적용

일대일: 대상 테이블에 외래키

  • 단방향

    • 단방향 관계는 JPA 지원 x
    • 양방향 관계는 지원



    • Member 엔티티에 있는 locker 참조로 반대쪽 테이블(LOCKER 테이블)에 있는 외래키(MEMBER_ID)를 관리할 수 없음
    • 단방향 관계는 JPA에서 지원하지 않고, 방법도 없음
      • 단방향 관계를 Locker에서 Member 방향으로 수정하거나, 양방향 관계로 만들고 Locker를 연관관계의 주인으로 설정해야 함
  • 양방향

    • 사실 일대일 주 테이블에 외래키 양방향과 매핑 방법이 같음
    • 일대일 관계는 내 엔티티에 있는 외래키를 직접 관리해야 함



    • 대상 엔티티의 참조를 외래키와 매핑하면 됨
      • Locker.member를 연관관계의 주인으로 잡아서 매핑

대상 테이블에 외래키가 있는 경우, 프록시 기능의 한계로 지연로딩으로 설정해도 항상 즉시로딩 됨

  • JPA 입장에서 프록시 객체를 만들기 위해서는 해당 엔티티의 참조에 매핑되는 값이 테이블에 있는지 없는지 알아야 함
    • 지연로딩 시, 참조에 값이 있으면 프록시로 초기화하고 값이 없으면 null을 넣어주기 때문
  • 주 테이블에 외래키가 있는 경우
    • 주 엔티티의 참조 변수에 매핑되는 값이 있는지 확인하기 위해 주 테이블만 검색하면 됨
    • 다른 대상 테이블을 검색할 필요가 없어서 지연로딩 가능
  • 대상 테이블에 외래키가 있는 경우
    • 주 테이블을 가져올 때, 해당 참조에 매핑되는 값이 있는지 확인하기 위해서 대상 테이블까지 검색해야 함
      • 주 테이블에 외래키 값이 없고, 외래키 값이 대상 테이블에 있기 때문에 매핑 되는 값을 확인하기 위해 대상 테이블을 검색해야 함
    • 결국 대상 테이블을 검색해야하기 때문에 지연로딩을 해도 즉시로딩이 되어버림(지연로딩으로 세팅하는 것이 의미가 없음)

◻ 다대다

다대다 관계 [N:M]

  • 실무에서 쓰지 않는 것을 권장함
  • 관계형 데이터베이스는 정규화된 테이블 2개로 다대다 관계를 표현할 수 없음
    • 연결 테이블을 추가해서 일대다, 다대일 관계로 풀어내야 함




  • 객체는 컬렉션을 사용해서 객체 2개로 다대다 관계가 가능함
  • @ManyToMany 사용하고,@JoinTable로 연결 테이블 지정해주어야 함
  • 다대다 매핑은 단방향과 양방향 모두 가능

다대다 매핑의 한계

  • 편리해보이지만 실무에서 사용 x

  • 연결 테이블이 단순 연결만 하고 끝나지 않음

    • 주문시간, 수량 같은 데이터가 들어올 수 있음
  • 중간 테이블이 숨겨져있기 때문에, 쿼리도 예상하지 못하게 날아감

  • 한계 극복

    • 연결 테이블용 엔티티 추가(연결 테이블을 엔티티로 승격)

    • @ManyToMany@OneToMany, @ManyToOne




  • 연결 테이블에서 PK

    • MEMBER_IDPRODUCT_ID를 복합키로 PK를 잡을 수 있음
      • JPA에서 복합키를 사용하면 Composite ID를 별도로 만들어주어야 함
    • 하지만 별도의 PK를 별도로 생성하는 것을 추천함
      • 객체 입장에서 비식별 관계를 사용하는 것이 단순하고 편리하게 ORM 매핑을 할 수 있음
  • 테이블간의 관계 설정

    • 식별 관계: 받아온 식별자는 기본키 + 외래키로 사용함
    • 비식별 관계: 받아온 식별자는 외래키로만 사용하고, 새로운 식별자를 추가함


Ref