연관관계 매핑 핵심 키워드
- 방향(Direction): [단방향, 양방향], 방향은 객체관계에만 존재하고 테이블 관계는 항상 양방향
- 다중성(Multiplicity): [다대일(N:1), 일대다(1:N), 일대일(1:1), 다대다(N:M)]
- 연관관계의 주인(Owner): 객체를 양방향 연관관계로 만들면 연관관계의 주인을 정해야 함.
❗️객체 연관관계와 테이블 연관관계의 가장 큰 차이
객체 연관관계 vs 테이블 연관관계
- 객체는 참조(주소)로 연관관계를 맺음(객체 그래프 탐색)
- 테이블은 외래 키로 연관관계를 맺음(조인)
@JoinColumn: 외래 키를 매핑할 때 사용
- 속성
- name: 매핑할 외래 키 이름
- referencedColumnName: 외래 키가 참조하는 대상 테이블의 컬럼명
- foreignKey(DDL): 외래 키 제약조건ㅇ르 직접 지정할 수 있음. 테이블 생성할 때만 사용
- unique, nullable, insertable, updatable, columnDefinition, table: @Column 속성과 같음
@ManyToOne: 다대일 관계에서 사용
- 속성
- optional: false로 설정하면 연관된 엔티티가 항상 있어야 함
- fetch: 글로벌 페치 전략 설정
- cascade: 영속성 전이 기능 사용
- targetEntity: 연관된 엔티티의 타입 정보 설정
JPA에서 엔티티를 저장할 때 연관된 모든 엔티티는 영속 상태여야 함
- 객체 그래프 탐색(객체 연관관계를 사용한 조회)
- 객체지향 쿼리 사용(JPQL)
참조하는 대상만 변경하면 나머지는 JPA가 자동으로 처리(변경 감지)
연관된 엔티티를 삭제하려면 기존에 있던 연관관계를 먼저 제거하고 삭제해야 함
- 양방향 매핑 시에는 무한 루프에 빠지지 않게 조심해야 함.
ex) Member.toString()에서 getTeam()을 호출하고 Team.getString()에서 getMember()를 호출하면 무한 루프에 빠질 수 있음.
연관관계의 주인: 두 객체 연관관계 중 하나를 정해서 테이블의 외래 키 관리
✅ 연관관계의 주인은 외래 키가 있는 곳!
// member와 team은 일대다(1:N)
team.getMembers().add(member1); // 무시(연관관계의 주인이 아님)
team.getMembers().add(member2); // 무시(연관관계의 주인이 아님)
member1.setTeam(team1); // 연관관계 설정(연관관계의 주인)
member2.setTeam(team1); // 연관관계 설정(연관관계의 주인)
객체 관점에서 양쪽 방향에 모두 값을 입력해주는 것이 가장 안전
public void testORM_양방향() {
// 팀1 저장
Team team1 = new Team("team1", "팀1");
em.persist(team1);
Member member1 = new Member("member1", "회원1");
// 양방향 연관관계 설정
member1.setTeam(team1);
team1.getMembers().add(member1);
em.persist(member1);
Member member2 = new Member("member2", "회원2");
// 양방향 연관관계 설정
member2.setTeam(team1);
team1.getMembers().add(member2);
em.persist(member2);
}
public class Member {
private Team team;
public void setTeam(Team team) {
this.team = team;
team.getMembers().add(this);
}
...
}
public void testORM_양방향_리팩토링() {
Team team1 = new Team("team1", "팀1");
em.persist(team1);
Member member1 = new Member("member1", "회원1");
member1.setTeam(team1); // 양방향 설정
em.persist(member1);
Member member2 = new Member("member2", "회원2");
member2.setTeam(team1); // 양방향 설정
em.persist(member2);
}
public void setTeam(Team team) {
// 기존 팀과의 관계를 제거
if (this.team != null) {
this.team.getMembers().remove(this);
}
this.team = team;
team.getMembers().add(this);
}