[JPA 기본] 단방향/양방향 연관관계
포스트
취소

[JPA 기본] 단방향/양방향 연관관계

연관관계가 필요한 이유

  • 객체를 테이블에 맞추어 데이터 중심으로 모델링하면 협력 관계를 만들 수 없다.
  • 테이블은 외래 키로 조인을 사용해서 연관된 테이블을 찾는다.
  • 객체는 참조를 사용해서 연관된 객체를 찾는다.

단방향 연관관계

  • 1:N의 관계에 있을 때 N에 해당하는 쪽에 @ManyToOne을 명시한다.
    • 예시
      • 1개의 팀에는 여러 명의 회원이 소속된다.
  • @JoinColumn 어노테이션의 name에는 FK가 될 칼럼의 이름을 명시한다.

  • 사용 엔티티
@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;
}

@Entity
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Team {
    @Id
    private Long id;
    private String name; 
}
  • 예시 코드
//s:연관관계 저장
    //팀 저장
    Team team = new Team();
    team.setName("TeamA");
    em.persist(team);

    //회원 저장
    Member member = new Member();
    member.setName("member1");
    member.setTeam(team); //단방향 연관관계 설정, 참조 저장
    em.persist(member);
//e:연관관계 저장

//e:객체 그래프 탐색
    //조회
    Member findMember = em.find(Member.class, member.getId()); 
    //참조를 사용해서 연관관계 조회
    Team findTeam = findMember.getTeam();
//e:객체 그래프 탐색

//s:연관관계 수정
    //새로운 팀 B
    Team teamB = new Team();
    teamB.setName("TeamB");
    em.persist(teamB);

    //회원1에 새로운 팀B 설정
    member.setTeam(teamB)
//e:연관관계 수정

양뱡향 연관관계

  • 1:N 관계일 때
    1에 해당하는 엔티티에는 N에 해당하는 엔티티의 List를, N에 해당하는 엔티티에는 1에 해당하는 엔티티의 class를 명시한다. (★)
  • add할때 NullPointException이 발생하는 것을 막기 위해 List는 ArrayList로 미리 초기화해둔다.
  • 순수 객체 상태를 고려해서 항상 양쪽에 값을 설정하자
  • 연관관계 편의 메소드를 생성하자
    • 예시
        //로직이 들어가는 경우에는 기존 Java의 관례때문에 setXXX보다는 다른 이름을 쓰는 것이 좋다.
        public void changeTeam(Team team){
            this.team = team;
            team.getMembers().add(this);
        }
        
  • 양방향 매핑시에 무한 루프를 조심하자
    • 예시
      • toString()
      • lombok
      • JSON 생성 라이브러리
  • 단방향 매핑만으로도 이미 연관관계 매핑은 완료된 것이나 마찬가지다.
  • 양방향 매핑은 객체 그래프 탐색 기능이 추가된 것 뿐이다.
    • JPQL에서 역방향으로 탐색할 일이 많다.
  • 단방향 매핑을 잘 하고 양방향은 필요할 때 추가해도 된다.
  • 테이블에 영향을 주지 않는다.
  • 왠만하면 컨트롤러에서 엔티티를 반환하면 안 된다. (★★★)

  • 사용 엔티티
@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;
}

@Entity
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Team {
    @Id
    private Long id;
    private String name;

    @OneToMany(mappedBy = "team") //연결할 객체명을 적어준다.
    private List<Member> members = new ArrayList<>()
}
  • 예시 코드
//조회
Team findTeam = em.find(Team.class, team.getId()); 
int memberSize = findTeam.getMembers().size(); //역방향 조회

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

  • 객체 연관관계
    • 종류
      • 회원 -> 팀 연관관계 1개 (단방향)
      • 팀 -> 회원 연관관계 1개 (단방향)
    • 특징
      • 객체의 양방향 관계는 사실 양방향 관계가 아니라 서로 다른 단뱡향 관계 2개다.
      • 객체를 양방향으로 참조하려면 단방향 연관관계를 2개 만들어야 한다.
  • 테이블 연관관계
    • 종류
      • 회원 <-> 팀의 연관관계 1개 (양방향)
    • 특징
      • 테이블은 외래 키 하나로 두 테이블의 연관관계를 관리한다.

연관관계의 주인

  • 객체의 두 관계중 하나를 연관관계의 주인으로 지정
  • 연관관계의 주인만이 외래 키를 관리(등록, 수정)
  • 주인이 아닌쪽은 읽기만 가능
  • 주인은 mappedBy 속성을 사용하지 않는다.
  • 주인이 아니면 mappedBy 속성으로 주인 지정
  • 왜래키가 있는 곳이 주인으로 정하는 것이 좋다. (★)

  • 사용 엔티티
    /*
        아래 코드의 경우에
        Member.team이 연관관계의 주인이 된다.
    */

    @Entity
    public class Member { 
        @Id
        @GeneratedValue
        private Long id;

        @Column(name = "USERNAME")
        private String name;

        private int age;

        @ManyToOne
        @JoinColumn(name = "TEAM_ID")
        private Team team; //← 연관관계의 주인 ★★★
    }

    @Entity
    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    public class Team {
        @Id
        private Long id;
        private String name;

        @OneToMany(mappedBy = "team")
        private List<Member> members = new ArrayList<>()
    }
    
  • 예시 코드
    Team team = new Team();
    team.setName("TeamA");
    em.persist(team);

    Member member = new Member();
    member.setName("member1");
    member.setTeam(team); //연관관계의 주인에 값 설정 ★★★

    team.getMembers().add(member);

    em.persist(member);
    

관련 용어

  • 방향(Direction)
    • 단방향
    • 양방향
  • 다중성(Multiplicity)
    • 다대일(N:1)
    • 일대다(1:N)
    • 일대일(1:1)
    • 다대다(N:M)
  • 연관관계의 주인(Owner)
    • 객체 양방향 연관관계는 관리 주인이 필요
    • C언어로 치면 포인터

출처

이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.