(2) 일대다
- 일대다 관계는 다대일 관계의 반대 방향이다.
- 일대다 관계는 엔티티를 하나 이상 참조할 수 있으므로 자바 컬렉션
Collection
,List
,Set
,Map
중에 하나를 사용해야 한다.
6.2.1 일대다 단방향
일대다 단방향 관계 매핑 시 @JoinColumn
을 명시해야 한다.
그렇지 않으면 JPA는 연결 테이블을 중간에 두고 연관관계를 관리하는 조인 테이블 전략을 기본으로 사용해서 매핑한다.
- 팀 엔티티의
Team.members
로 회원 테이블의 외래 키TEAM_ID
를 관리한다.
- 보통은 자신이 매핑한 테이블의 외래 키(다 쪽)를 관리하지만,
Member
엔티티에는 외래 키를 매핑할 수 있는 참조 필드가 없다.
- 따라서 반대편 테이블의 외래 키를 관리하는 특이한 모습이 나타난다.
일대다 단방향 매핑의 단점
매핑한 객체가 관리하는 외래 키가 다른 테이블에 있다는 점
=> 연관관계 처리를 위한 UPDATE SQL을 추가로 실행해야 한다.
*본인 테이블에 외래 키가 있으면 엔티티 저장과 연관관계 처리를 INSERT SQL 한 번에 가능*
public void testSave() {Member member1 = new Member("member1");Member member2 = new Member("member2");Team team1 = new Team("team1");team1.getMembers().add(member1);team1.getMembers().add(member2);em.persist(member1); // INSERT-member1em.persist(member2); // INSERT-member2em.persist(team1); // INSERT-team1, UPDATE-member1.fk,// UPDATE-member2.fktransaction.commit();}
실행한 결과 SQL
insert into Member (MEMBER_ID, username) values (null, ?)insert into Member (MEMBER_ID, username) values (null, ?)insert into Team (TEAM_ID, name) values (null, ?)update Member SET TEAM_ID=? where MEMBER_ID=?update Member SET TEAM_ID=? where MEMBER_ID=?
Member 엔티티는 Team 엔티티를 모른다.
연관관계 정보는 Team
엔티티의 members
가 관리하기 때문에 Member
엔티티를 저장할 때 MEMBER
테이블의 TEAM_ID
외래 키에 아무 값도 저장되지 않는다.
대신 Team
엔티티를 저장할 때 Team.members
의 참조값을 확인해서 회원 테이블에 있는 TEAM_ID
외래 키를 업데이트한다.
일대다 단방향 매핑 대신 다대일 양방향 매핑을 사용하자
일대다 단방향 매핑 사용 시 엔티티를 매핑한 테이블이 아닌 다른 테이블의 외래 키를 관리해야 한다.
=> 성능 문제 + 관리가 부담스러움
따라서 일대다 단방향 매핑 대신 다대일 양방향 매핑을 사용한다.
다대일 양방향 매핑은 관리해야 하는 외래 키가 본인 테이블에 있다. -> 일대다 단방향 매핑 같은 문제가 발생 X
따라서 다대일 양방향 매핑 사용을 권장한다.
일대다 단방향 팀 엔티티
@Entitypublic class Team {@Id@GeneratedValue@Column(name = "TEAM_ID")private Long id;private String name;@OneToMany(mappedBy = "team")@JoinColumn(name = "TEAM_ID") // MEMBER 테이블의 TEAM_ID (FK)private List<Member> members = new ArrayList<Member>();// Getter, Setter ......}
일대다 단방향 회원 엔티티
@Entitypublic class Member {@Id @GeneratedValue@Column(name = "MEMBER_ID")private Long id;private String username;// Getter, Setter ......}
6.2.2 일대다 양방향
일대다 양방향 매핑은 존재하지 않는다. 대신 다대일 양방향 매핑
을 사용 (사실 같은 말이지만 왼쪽을 연관관계의 주인으로 가정하여 분류했기 때문에)
양방향 매핑에서
@OneToMany
는 연관관계의 주인이 될 수 없다.=> 관계형 데이터베이스 특성상 일대다, 다대일 관계는 항상 다쪽에 외래 키가 있기 때문
따라서 @ManyToOne
을 사용한 곳이 다 쪽이고, mappedBy
속성이 없다.
일대다 단방향 매핑 반대편에 같은 외래 키를 사용하는 다대일 단방향 매핑을 읽기 전용으로 하나 추가하면 가능하긴 함.
일대다 양방향 팀 엔티티
@Entitypublic 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<Member>();// Getter, Setter ...}
일대다 양방향 회원 엔티티
@Entitypublic 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;// Getter, Setter ......}
둘 다 같은 키를 관리해서 문제가 발생할 수 있기 때문에 반대편 다대일 쪽은 insertable = false, updatable = false
로 설정해서 읽기만 가능하게 한다.
일대다 양방향 매핑
이라기 보다는 일대다 단방향 매핑
반대편에 다대일 단방향 매핑
을 읽기 전용으로 추가해서 일대일 양방향
처럼 보이도록 하는 방법
따라서 일대다 단방향 매핑이 가지는 단점을 그대로 가진다.
될 수 있으면 다대일 양방향 매핑을 사용하자