연관관계 매핑
연관관계 매핑 시 고려해야 할 3가지가 다중성, 단방향 or 양방향, 연관관계의 주인이라고 저번 게시글에 작성하였다.
- 이 게시글에서는 다중성에 대해 알아본다.
- JPA에서는 연관관계를 맺을 때 다중성을 annotation으로 표기한다.
- @ManyToMany, @ManyToOne, @OneToOne, @OneToMany으로 이름만 봐도 명확하게 이해가 가능하다.
일대일(@OneToOne)
- 일대일 연관관계 매핑은 저번 게시글에서 다룬 거처럼 외래 키를 원하는 곳에 두면 된다.
다대일(@ManyToOne)
- 다대일의 반대는 일대다이다. 그러므로 다대일 매핑을 하면 상대편 객체에서 일대다로 매핑을 하는 것과 같다.
- 실제 테이블에서는 다 쪽이 외래 키를 가지게 된다.
- 그러므로 다대일 매핑을 하는 게 명확하게 대상 객체에 값을 넣는 것을 인지할 수 있으므로 많이 사용된다.
// Lombok Annotation은 따로 생략했습니다.
@Entity
public class Member {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "member_id")
private Long id;
private String name;
@OneToMany(mappedBy = "member")
private List<Board> boards = new ArrayList<>();
}
@Entity
public class Board {
@Id
@GeneratedValue
@Column(name = "board_id")
private Long id;
private String title;
private String content;
@ManyToOne
@JoinColumn(name = "member_id")
private Member member;
}
- 위의 예제는 Member는 Board를 많이 가질 수 있고 각 Board는 Member를 한 명만 가질 수 있으므로 Board 입장에서는 다대일 관계이다.
- 그러므로 Board에서 다대일 매핑을 하였다.
- 실제 테이블에서도 Board가 외래 키를 가지게 되므로 연관관계 주인이 실제 외래키를 가지게 된다.
Hibernate:
create table Board (
board_id bigint not null,
content varchar(255),
title varchar(255),
member_id bigint,
primary key (board_id)
)
Hibernate:
create table Member (
member_id bigint generated by default as identity,
name varchar(255),
primary key (member_id)
)
Hibernate:
alter table Board
add constraint FK3hi0ewxk20gygxyqrp5t5vudr
foreign key (member_id)
references Member
일대다(@OneToMany)
- 위의 예제에서 Member를 연관관계의 주인으로 잡으면 일대다 매핑이 된다.
- 하지만 테이블에서는 1:N 관계에서 N에게 외래 키가 생긴다.
- 그렇기 때문에 일대다 매핑을 하게 되면 특이한 구조가 된다.
- 외래 키를 관리하는 테이블이 반대편이므로 추가 Update SQL이 날아간다.
- 일대다 매핑은 연관관계의 주인인으로 잡지 말고 항상 다대일 매핑으로 하자
다대다(@ManyToMany)
- 관계형 데이터베이스는 정규화된 테이블 두 개로 다대다 관계를 표현할 수 없다.
- 그렇기 때문에 따로 테이블을 만들어 풀어낸다.
@Entity
public class Member {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "member_id")
private Long id;
private String name;
@ManyToMany(mappedBy = "members")
private List<Team> teams = new ArrayList<>();
}
@Entity
public class Team {
@Id
@GeneratedValue
@Column(name = "team_id")
private Long id;
private String teamName;
@ManyToMany
@JoinColumn(name = "member_id")
private List<Member> members = new ArrayList<>();
}
Hibrnate:
create table Member (
member_id bigint generated by default as identity,
name varchar(255),
primary key (member_id)
)
Hibernate:
create table Team (
team_id bigint not null,
teamName varchar(255),
primary key (team_id)
)
Hibernate:
create table Team_Member (
teams_team_id bigint not null,
members_member_id bigint not null
)
- Member와 Team을 다대다 관계로 매핑을 하였다.
- 위에서 설명했듯이 관계형 DB에서는 아래와 같이 따로 테이블을 생성하는 것을 알 수 있다.
- 객체에서는 두 개지만 실제 DB에서는 3개의 테이블로 관리되면 유지보수가 힘들어진다.
- 그러므로 다대다 매핑을 하기보다는 중간에 연결할 Entity 하나를 생성하고 일대다 - 중간 Entity - 다대일 이렇게 관계를 맺는 게 좋다.
중간 Entity 만들기
- MemberTeam이라는 중간 Entity를 만들었다.
- 그리고 거기에서 각 Member, Team을 다대일 관계로 매핑하였다
- 실제 테이블 생성은 위와 비슷하게 생겨날 것이다.
- 하지만 명시적으로 MemberTeam 객체가 존재하므로 혼란이 적어진다.
- 그러므로 다대다 관계보다는 중간 Entity를 만들어 일대다 - 다대일로 관계를 맺자.
@Entity
public class Team {
@Id
@GeneratedValue
@Column(name = "team_id")
private Long id;
private String teamName;
@OneToMany(mappedBy = "team")
private List<MemberTeam> memberTeams = new ArrayList<>();
}
@Entity
public class Member {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "member_id")
private Long id;
private String name;
@OneToMany(mappedBy = "member")
private List<MemberTeam> memberTeams = new ArrayList<>();
}
@Entity
public class MemberTeam {
@Id
@GeneratedValue
@Column(name = "member_team_id")
private Long id;
@ManyToOne
@JoinColumn(name = "member_id")
private Member member;
@ManyToOne
@JoinColumn(name = "team_id")
private Team team;
}
결론
- 일대일 관계는 어느 쪽에 연관관계 주인을 두던지 상관이 없다.
- 다대일 관계는 연관관계 주인인 다 쪽에서 외래 키를 관리하므로 명확하다.
- 일대다 관계는 사용하지 말고 반대로 다대일 관계로 풀어서 사용하자
- 다대다 관계는 사용하지 말고 일대다 - 중간 Entity - 다대일 관계로 풀어서 사용하자.
'JPA' 카테고리의 다른 글
JPA 연관관계 매핑 4. 프록시 (0) | 2020.01.23 |
---|---|
JPA 연관관계 매핑 3. 상속관계 매핑 (0) | 2020.01.22 |
JPA 연관관계 매핑 1. 단방향 양방향 연관관계 이해하기 (0) | 2020.01.22 |
JPA 엔티티 매핑: DB 스키마 자동 생성, 필드와 칼럼 매핑, 기본키 매핑 (0) | 2020.01.06 |
JPA와 JPA 영속성 컨텍스트, 플러시와 준영속 상태 (0) | 2020.01.06 |