[JPA] 일대다[1:N] 연관관계 매핑
📌 일대다 [1:N]
✔ 목차
- 연관관계 매핑시 고려사항 3가지
- 다대일 [N:1]
- 일대다 [1:N] 🚀
- 일대일 [1:1]
- 다대다 [N:M]
- 실전 예제 - 3. 다양한 연관관계 매핑
✔ 일대다 매핑을 이해하기 위한 다대일 양방향 매핑
- 위 사진을 보면 팀과 맴버가 다대일 양방향 관계이다.
- 현재는 Team도 members를 가지고 있고, Member 역시 team을 가지고 있다.
- 하지만 Member 입장에서는 Team을 참조하지 않아도 되는 설계가 나올 수 있다.
- 위와 같은 이유로 인해 아래 사진과 같은 연관관계 설계가 나올 수 있다.
- 일대다 단방향 매핑 관계
- 객체 입장에서 생각하면 충분히 나올 수 있는 설계다.
✔ 일대다 단방향 매핑
위에서 본 관계와 비교하여 화살표 방향이 Team –> Member로
반대가 된것을 잘 봐야한다.
- 일대다 관계에서는 일(1)이 연관관계의 주인이다.
- 일(1) 쪽에서 외래키를 관리하겠다는 의미가 된다.
- 일대다 관계는 JPA 표준스펙에서는 지원을 하지만 실무에서 해당 모델은 권장하지 않는다.
- 일대다 단방향 연관관계는 반드시 @JoinColumn을 사용해야 한다.
- 사용하지 않는 경우 JoinTable 방식을 사용한다.
- Team_Member라는 중간 테이블이 생성된다.
💡 일대다 단방향 매핑의 문제점 01
- 테이블 일대다 관계는 항상 다(N) 쪽에 외래키가 있다.
- 객체와 테이블의 패러다임 차이 때문에 객체의 반대편 테이블에 외래키를 관리하는 특이한 구조.
- 현재 TEAM과 MEMBER 테이블 중 외래키는 MEMBER 테이블에 존재하는 상황이다.
- 아래 예제를 한번 살펴보자.
Member 엔티티
@Entity
public class Member {
@Id
@GeneratedValue
@Column(name = "MEMBER_ID")
private Long id;
@Column(name = "USERNAME")
private String username; //Member에는 일대다 단방향 관계기에 별다른 제스처가 없다
Team 엔티티
@Entity
public class Team {
@Id
@GeneratedValue
@Column(name = "TEAM_ID")
private Long id;
private String name;
@OneToMany
@JoinColumn(name = "TEAM_ID") //@JoinColumn을 통해 TEAM_ID를 연관관계의 주인으로 설정
private List<MemberSample> members = new ArrayList<>();
}
JPAMain 클래스
try {
Member member = new Member();
member.setUsername("member1");
em.persist(member); // 영속성 컨텍스트 -> 맴버 등록
System.out.println("====맴버 등록===="); // 팀 등록 전
Team team = new Team();
team.setName("teamA");
team.getMembers().add(member); // 여기가 문제가 된다
em.persist(team);
System.out.println("====팀 등록====");
tx.commit();
} catch (Exception e) {
tx.rollback();
} finally {
em.close();
}
- 현재 Team 객체의 member를 업데이트하고 있는데, 위에서 말했다시피 외래키는 MEMEBER 테이블에 존재한다.
💡 일대다 단방향 매핑의 문제점 02
JPAMain 클래스 구동 결과
Hibernate:
/* insert hello.jpa.Member
*/
insert
into
Member
(id, age, createdDate, description, lastModifiedDate, roleType, name)
values
(null, ?, ?, ?, ?, ?, ?)
-----멤버 저장
Hibernate:
/* insert hello.jpa.Team
*/
insert
into
Team
(id, name)
values
(null, ?)
-----팀 저장
Hibernate:
/* create one-to-many row hello.jpa.Team.members */
update
Member
set
TEAM_ID=?
where
id=?
- 팀 엔티티를 수정하였는데 맴버 테이블에 업데이트 쿼리가 나가는 현상 발생.
- 실무에서는 테이블이 수십개가 엮여서 돌아가는 상황, 위와 같은 상황은 운영을 힘들게 한다.
💡 해결 방안
- 객체지향적으로 조금 손해를 볼 수 있지만, 다대일 단방향, 양방향을 사용한다.
- 즉, 먼저 학습한 다대일 단방향 관계로 매핑하고, 필요한 경우 양방향 매핑을 통해 해결한다.
- 객체 입장에서 보면, 반대방향으로 참조할 필요가 없는데 관계를 하나 만드는 것이지만, DB의 입장으로 설계의
방향을 조금 더 맞춰서 운영상 유지보수하기 쉬운 쪽으로 선택할 수 있다.
✔ 일대다 양방향
- JPA 표준 스펙이 제공하는것은 아니지만, 일대다 양방향 매핑 역시 존재한다.
//수정 전: 이런식으로 매핑을 하면 연관관계의 주인이 2개가 된다.
@ManyToOne
@JoinColumn(name = "TEAM_ID")
private Team team;
//수정 후: 읽기 전용으로 변경하여, 양방향 매핑을 한 효과를 만들어낸다.
@ManyToOne
@JoinColumn(name = "TEAM_ID", insertable = false, updatable = false)
private Team team;
🚀 결론
- 일대다 단방향 매핑의 단점
- 엔티티가 관리하는 외래키가 다른 테이블에 있음
- 연관관계 관리를 위해 추가로 UPDATE SQL 실행. (성능 문제)
- 일대다 단방향 매핑보다는 다대일 양방향 매핑을 사용하자.
- 객체간의 참조를 하나 더 넣는 한이 있더라도 다대일 사용.
댓글남기기