[JPA] 다양한 연관관계 매핑

Updated:

들어가며

해당 글은 자바 ORM 표준 프로그래밍을 정리한 글입니다.

엔티티의 연관관계를 매핑할 때는 3가지를 고려해야 한다.

  • 다중성
  • 단방향, 양방향
  • 연관관계의 주인

다중성

연관관계에는 다음과 같은 다중성이 있다.

  • 다대일(@ManyToOne)
  • 일대다(@OneToMany)
  • 일대일(@OneToOne)
  • 다대다(@ManyToMany)

단반향, 양방향

  • 객체 관계에서 한 쪽만 참조하는 것을 단방향 관계라고 하고, 양쪽이 서로 참조하는 것을 양방향 관계라고 한다.

연관관계의 주인

  • 다(N)쪽이 연관관계의 주인이라고 생각하면 된다.

다대일

다대일 단방향

@Entity
public class Member {
    @Id @GeneratedValue
    @Column(name = "member_id")
    private Long id;
    @ManyToOne
    @JoinColumn(name = "team_id")
    private Team team;
}

@Entity
public class Team {
    @Id
    @GeneratedValue
    @Column(name = "team_id")
    private Long id;
}

멤버와 팀 간에 다대일 단방향으로 연결하였다. 멤버에서는 팀으로 참조가 가능하지만 팀에서는 멤버를 참조하지 못 한다.

다대일 양방향

@Entity
public class Member {
    @Id @GeneratedValue
    @Column(name = "member_id")
    private Long id;
    @ManyToOne
    @JoinColumn(name = "team_id")
    private Team team;
}

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

}

@OneToMany를 추가하여 팀과 멤버 간에 다대일 양방향 관계를 형성하였다.

  • 양방향은 외래키가 있는 쪽이 연관관계의 주인이다.(@ManyToOne쪽이 주인)
  • 양방향 연관관계는 항상 서로를 참조해야 한다.
  • 양방향 관계의 경우 서로를 참조하기 때문에 무한루프에 빠지지 않도록 주의해야 한다.

일대다

일대다 단방향

@Entity
public class Member {
    @Id @GeneratedValue
    @Column(name = "member_id")
    private Long id;
}

@Entity
public class Team {
    @Id
    @GeneratedValue
    @Column(name = "team_id")
    private Long id;
    @OneToMany
    @JoinColumn(name = "team_id") // member 테이블의 team id(FK)
    private List<Member> members = new ArrayList<Member>();
}
  • 일대다 단방향 매핑의 단점
    • 다른 테이블에 외래키가 있을 경우 INSERT후 UPDATE SQL을 추가로 실행 해야 한다.
  • 일대다 단방향 매핑보다는 다대일 양방향 매핑을 권장한다.

일대일[1:1]

주 테이블에 외래키

@Entity
public class Member {
    @Id @GeneratedValue
    @Column(name = "member_id")
    private Long id;
    @OneToOne
    @JoinColumn(name = "locker_id")
    private Locker locker;
}

@Entity
public class Locker {
    @Id @GeneratedValue
    @Column(name = "locker_id")
    private Long id;
}
Hibernate: 
    create table locker (
       locker_id bigint not null,
        primary key (locker_id)
    )
Hibernate: 
    
    create table member (
       member_id bigint not null,
        locker_id bigint,
        team_id bigint,
        primary key (member_id)
    )
Hibernate: 
    alter table member 
       add constraint FKr7t81ncnhbcknj50yeyf0efk9 
       foreign key (locker_id) 
       references locker    
  • 주 객체가 대상 객체를 참조하는 방식, Member 엔티티가 Locker의 외래키를 갖고 있다.
  • 객체지향 개발자들이 선호하는 방식이고, 주 테이블이 외래키를 갖고 있으므로 주 테이블만 확인해도 대상 테이블과 연관관계가 있는지 알 수 있다.
  • 해당 관계는 다대일 단방향과 거의 비슷한 구조다.

대상 테이블에 외래 키

@Entity
public class Member {
    @Id @GeneratedValue
    @Column(name = "member_id")
    private Long id;
}

@Entity
public class Locker {
    @Id @GeneratedValue
    @Column(name = "locker_id")
    private Long id;
    @OneToOne
    @JoinColumn(name = "member_id")
    private Member member;    
}
  • 전통적인 데이터베이스 개발자들 방식
  • 일대일에서 일대다로 변경할 때 테이블 구조를 그대로 유지 할 수 있음

다대다[N:N]

  • 다대다 보다는 일대다와 다대일로 분리하는걸 권장
  • 다대다로 사용하다가 중간 테이블에 새로운 필드가 추가되는 경우가 발생하면 표현이 불가능

복합 기본키

  • 복합 키는 별도의 식별자 클래스로 만들어야 한다.
  • Serializable을 구현해야 한다.
  • equals와 hashCode 메소드를 구현해야 한다.
  • 기본 생성자가 있어야 한다.
  • 식별자 클래스는 public이어야 한다.
  • @IdClass를 사용하는 방법 외에 @EmbeddedId를 사용하는 방법도 있다.
@Entity
@IdClass(MemberProductId.class)
public class MemberProduct {
    @Id @ManyToOne
    @JoinColumn(name = "member_id")
    private Member member; // MemberProductId.member와 연결

    @Id @ManyToOne
    @JoinColumn(name = "product_id")
    private Product product; // MemberProductId.product와 연결

    public static class MemberProductId implements Serializable {
        private String member; // MemberProduct.member와 연결
        private String product; // MemberProduct.product와 연결
    }
}
    create table member_product (
      member_id varchar(255) not null,
      product_id varchar(255) not null,
      primary key (member_id, product_id)
    )
    
    alter table member_product 
       add constraint FKf6dbpy382ot6da9a62yp6e2ty 
       foreign key (member_id) 
       references member    
       
    alter table member_product 
       add constraint FKa5xnfg8uts10iwrd5nmdfhoxk 
       foreign key (product_id) 
       references product       

식별 키

  • 식별키의 경우 대상 테이블의 PK를 자신의 기본키로 사용하는 방식

비식별 키

  • 비식별 키의 경우 대상 테이블의 PK를 외래키로만 사용하고 신규 PK를 생성해서 사용하는 방식
  • JPA에서는 비식별 키를 사용하는 경우가 많다

마치며

이번 시간에는 다양한 연관관계 매핑에 대해서 알아보았다. 일대일, 다대일, 일대다의 경우는 많이 사용되고 있으나 다대다의 경우는 실무에서 많이 사용하지 않으므로 넘어갔지만 다대다까지 알고 싶다면 해당 책에 내용을 확인하는 것을 추천한다.



Leave a comment