본문 바로가기
ORM(JPA)

[JPA] 연관관계 매핑 - N:1(다대일), 1:N(일대다)

by hjhello423 2020. 1. 31.

[스프링/JPA] - [JPA] 연관관계 매핑 - 1:1(일대일), N:N(다대다)


JPA는 다양한 연결 관계가 있습니다.

이 연결 관계들에 대해 하나씩 알아보겠습니다.

고려할 점

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

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

위의 3가지 사항을 서로 조합하여 생기는 경우의 수를 가지고 연관관계를 설정해야 합니다.

예를 들어 N:1 단방향이나 N:N 양방향 관계와 같은 경우를 만들 수 있습니다.

N:1 (다대일)

데이터 테이블의 1, N 관계에서 외래 키는 항상 다쪽(N)에 있습니다.

따라서 양방향의 경우 항상 N 쪽이 연관관계의 주인입니다.

이 말은 즉 양방향일 경우에는 @OneToMany를 선언한 쪽에서 mappedBy속성을 지정해야 한다는 뜻이 됩니다.

자세한 내용은 순서대로 소스와 함께 다대일 관계의 단방향/양방향에 대해 살펴보겠습니다.

N:1 단방향

먼저 회원 엔티티와(Member) 팀 엔티티(Team)에 대해서 연관 관계를 살펴보겠습니다. 

 

 

 

 

 

 

회원은 N, 팀은 1을 나타내는 N:1 단방향을 보여주고 있습니다.

다쪽인 회원에서 팀의 FK(외래 키)를 관리하고 있는 모습을 볼 수 있습니다.

이를 객체로 풀어보면 아래와 같습니다.

<Member.java>

@Entity
public class Member {

    @Id
    @GeneratedValue
    @Column(name = "member_id")
    private Long id;

    private String userName;

    @ManyToOne
    @JoinColumn(name = "team_id")
    private Team team;
}

 

<Team.java>

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

    private String name;
}

회원은 Member.team을 통해 팀 엔티티를 참조하고 있습니다.

@JoinColumn(name = "team_id")을 사용해서 Member.team 필드를 TEAM_ID FK와 매핑하였습니다.

N:1 양방향

이번엔 회원과 팀이 양방향 관계를 가지는 상황을 살펴보겠습니다.

아래 관계에서는 회원이 연관관계의 주인입니다.

 

 

 

 

앞서 설명했듯이 FK(외래 키)는 항상 다(N)에 존재하고 있고, FK를 가진 쪽이 연관관계의 주인이 됩니다.

위에서는 Team의 FK를 가지 Member가 연관관계의 주인이 됩니다.

 

이제 소스코드를 살펴보겠습니다. 회원 엔티티는(Member) 변화가 없습니다.

<Team.java>

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

    private String name;

    @OneToMany(mappedBy = "team")
    private List<Member> members = new ArrayList<>();
}

팀 엔티티에서 회원을 참조하기 위해 팀 엔티티에 회원과 관련된 필드를(members) 추가하였습니다.

팀-회원 관계에서는 1:N의 관계를 가지므로 @OneToMany를 사용하여 관계를 매핑해 주었습니다.

양방향 연관관계에서는 항상 관계의 주인이 필요한데 실재 FK를 가지고 있는 회원이 관계의 주인이기 때문에 팀 엔티티에 @OneToMany(mappedBy = "team")처럼 mappedBy 속성에 회원 엔티티의 FK를 매핑한 필드명을 지정해 연관 관계의 주인이 회원이라는 것으로 선언해 주었습니다.

1:N (일대다)

1:N 관계는 엔티티를 1개 이상 참조하므로 컬렉션을 이용해 표현합니다.

Collection, List, Map 중 하나를 사용합니다.

 

1:N 단방향

위에서 본 관계와 비교하여 화살표 방향이 반대가 된것을 잘 봐주세요.

위의 그림을 보면 팀 엔티티의 Team.members로 회원 테이블의 TEAM_ID FK를 관리하고 있습니다.

보통 자신이 매핑한 테이블의 FK를 관리 합니다.

하지만 이 매핑은 반대 테이블의 FK를 관리고 있습니다.(membersTEAM_ID를 관리)

팀 엔티티에서 회원 엔티티를 참조하지만, 실제 테이블의 FK는 MEMBER 테이블에 있기 때문에 생기는 모습 입니다.

<Member.java>

@Entity
public class Member {

    @Id
    @GeneratedValue
    @Column(name = "member_id")
    private Long id;

    private String userName;
}

 

<Team.java>

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

    private String name;

    @OneToMany
    @JoinColumn(name = "team_id")
    private List<Member> members = new ArrayList<>();
}

@JoinColumn(name = "team_id")을 명시하여 1:N 단방향 관계를 매핑하고 있습니다.

@JoinColumn을 사용하지 않는다면 JPA는 자동으로 두 연관관계를 관리하는 Join 테이블 전략을 사용하여 매핑 합니다.

 

이 관계의 단점은 매핑한 객체가(Team)관리하는 FK가 다른 테이블에(MEMBER) 있다는 점 입니다.

본인 테이블에 FK가 있으면 insert 쿼리 한번으로 끝나지만, 서로 다른 테이블에 있으므로 별도의 update 쿼리를 추가로 실행하게 됩니다.

 

이러한 문제를 해결하기 위해 1:N 단방향 보다는 N:1 양방향 관계를 설정하여 해결하는 방법이 좋습니다.

N:1 양방향으로 설정하더라도 테이블의 변경은 일어나지 않습니다.

1:N 양방향

1:N 양방향 매핑은 존재하지 않습니다. 양방향 매핑에서 @OneToMany는 주인이 될 수 없기 때문 입니다.

쉽게 말해 FK는 항상 N쪽에 있기 때문입니다.

1:N 양방향 대신 N:1 양항향 매핑을 사용는것을 추천 합니다.

물론 1:N 양방향 구현이 불가능 한것은 아닙니다. 바로 읽기 전용 매핑을 이용하는 방법을 이용해서 구현이 가능 합니다.

 

<Member.java>

@Entity
public class Member {

    @Id
    @GeneratedValue
    @Column(name = "member_id")
    private Long id;

    private String userName;

    @ManyToOne
    @JoinColumn(name = "team_id", insertable = false, updatable = false)
    private Team team;
}

 

<Team.java>

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

    private String name;

    @OneToMany
    @JoinColumn(name = "team_id")
    private List<Member> members = new ArrayList<>();
}

회원 엔티티에서 N:1 단방향 매핑을 추가하였습니다.

1:N 단방향 매핑과 같은 tram_id FK를 매핑하였는데, 이렇게 하면 둘 다 같은 키를 관리하므로 문제 발생 가능성이 있습니다.

N:1 쪽은(회원) 읽기만 가능하도록 설정하여 이 문제를 해결 가능 합니다.

 


1:1, N:N 관계에 대해서는 아래 링크에서 설명하였습니다.

[스프링/JPA] - [JPA] 연관관계 매핑 - 1:1(일대일), N:N(다대다)


참고

자바 ORM 표준 JPA 프로그래밍, 김영한, 에이콘

 

반응형

댓글