일대 다(1:N) 관계
JPA가 지원하는 연관관계 매핑중 일대 다 관계의 단방향, 양방향 특징과, 매핑 방법, 장단점에 대해 공부해 보려고 한다.
다(N)보다 일(1)쪽에 비즈니스 로직의 비중이 높거나 중요도가 높은 경우 고려할 수 있는 관계이다.
일대 다 관계에서는 일(1) 쪽인 TEAM의 members 프로퍼티와 MEMBER테이블의 MEMBER_ID를 매핑해 Team 엔티티의 members 프로퍼티에서 외래키를 관리한다.
특징
- 일(1)이 되는 쪽이 연관관계의 주인이 된다.
- 테이블 관계는 항상 다(N) 쪽에 외래키가 있다.
- 객체와 테이블의 페러다임 불일치로 인해 반대편 테이블의 외래 키를 관리한다.
- 일대 다 단방향, 일대 다 양방향 관계가 있지만 표준에서는 단방향만 지원한다.
- @JoinColumn 사용이 필수가 된다.
@OneToMany
1:N 관계를 표현하기 위해선 엔티티의 외래키를 관리할 프로퍼티 위에 @OneToMany 어노테이션을 사용하여 관계를 명시한다. 1:N 관계에서 @JoinColumn을 사용해서 매핑할 컬럼을 명시하지 않은 경우 중간에 테이블 하나를 추가하는 조인 테이블 방식을 디폴트로 사용한다.
아래 예제를 통해 알아보자
@JoinColumn 사용하지 않은 경우
@Entity
public class Team {
@Id @GeneratedValue
private Long id;
private String name;
@OneToMany
private List<Member> members;
Hibernate:
create table Member (
id bigint not null,
TEAM_ID bigint,
username varchar(255),
primary key (id)
)
Hibernate:
create table Team (
id bigint not null,
name varchar(255),
primary key (id)
)
/*조인 테이블 방식의 테이블 생성 */
Hibernate:
create table Team_Member (
Team_id bigint not null,
members_id bigint not null
)
Hibernate:
alter table Team_Member
add constraint UK_s2bdqsiefihvxghf2fujxn2eg unique (members_id)
Hibernate:
alter table Team_Member
add constraint FKbh0ppoyhmwjgigc2gs7lr1kcf
foreign key (members_id)
references Member
Hibernate:
alter table Team_Member
add constraint FK267gfjokmdychsnik3u313q5j
foreign key (Team_id)
references Team
@JoinColumn을 사용하지 않은 경우 create모드에서 MEMBER테이블과 TEAM 테이블 외에 자동으로 Team_meber라는 join테이블과 제약 조건을 생성하는 DDL이 실행 된 것을 볼 수 있다.
DB에도 TEAM_MEMBER 테이블 생성되어있다.
따라서 참조 테이블 방식을 사용할 의도가 아니면 @JoinColumn 어노테이션에 매핑할 컬럼 을 반드시 명시해 주어야한다.
단점
- FK(forigen key)를 관리하는 엔티티와 반대의 테이블이 매핑되어 동작이 직관적이지 않아 관리가 까다롭다.
아래 코드에서 Team 엔티티에 Member를 추가하지만 실행 결과를 보면 Member 테이블의 memberId가 수정된다.
Team team = new Team(); team.setName("teamA"); em.persist(team); Member member = new Member(); em.member em.persist(member); // 팀 엔티티에 member 정보 추가 team.getMembers().add(member);
Hibernate: /* insert hellojpa.Team */ insert into Team (name, id) values (?, ?) Hibernate: /* insert hellojpa.Member */ insert into Member (TEAM_ID, username, id) values (?, ?, ?) Hibernate: /* create one-to-many row hellojpa.Team.members */ update Member set MEMBER_ID=? where id=?
- Team엔티티에서 참조 객체 Member를 다룰 때 update문이 추가적으로 발생하므로 결과가 같은 N:1 연관관계에 비해 성능적으로 떨어진다.
일대 다(1:N) 관계 - 양방향
JPA 표준에선 지원하지 않지만 엔티티 설정을 통해 양방향 관계와 동일하게 동작하도록 설정 할 수 있다.
@JoinColumn(insertable = false, updatable = false)
Member 엔티티의 team 프로퍼티를 만들고 team의 Id를 참조 할 수 있도록 @ManyToOne 어노테이션을 명시한다.
이 때 속성 값으로 insertable 과 updatable을 지정 할 수 있는데, 둘 다 false로 지정할 경우 프로퍼티를 통해 테이블의 쓰기와 수정이 불가능해져 양방향 연관관계와 동일해진다.
@Entity
public class Member {
@Id @GeneratedValue
private Long id;
@ManyToOne()
@JoinColumn(name = "TEAM_ID", insertable = false, updatable = false)
private Team team;
단 공식적으로 존재하는 방법이 아니고, N:1 양방향 관계 매핑으로 사용하는 것이 성능적으로 객체지향적으로 이점이 많기 때문에 양방향 엔티티를 사용하는 것이 바람직하다.
'JAVA' 카테고리의 다른 글
[JPA] 영속성 컨텍스트의 이점 - 동일성 보장 (0) | 2024.01.10 |
---|---|
[JPA] 영속성 컨텍스트의 이점 - 1차 캐시 (1) | 2024.01.10 |
[JPA] 엔티티의 생명주기 - 영속, 비영속, 준영속, 삭제 (0) | 2024.01.10 |
[JPA] 엔티티 매핑 - 기본 키 매핑 (0) | 2024.01.06 |
[JPA] JPA 필드와 컬럼 매핑 (0) | 2024.01.05 |