Table of Contents
단방향 연관관계
- 객체 연관관계와 테이블 연관관계의 가장 큰 차이
객체 참조를 통한 연관관계는 언제나 단방향이다. 객체간에 연관관계를 양방향으로 만들고 싶으면 반대쪽에도 필드를 추가해서 참조를 보관해야 한다.
즉, 양방향 관계가 아니라 서로 다른 단방향 관계 2개다. - 객체 연관관계 VS 테이블 연관관계 정리
- 객체는 참조로 연관관계를 맺는다.
- 테이블은 외래 키로 연관관계를 맺는다.
@JoinColumn
외래 키를 매핑할 때 사용한다.
- name : 매핑할 외래 키 이름, 필드명 + _ + 참조하는 테이블의 기본 키 컬럼명
- referencedColumnName : 외래 키가 참조하는 대상 테이블의 컬럼명, 참조하는 테이블의 기본키 컬럼명
- foreignKey(DDL) : 외래 키 제약조건을 직접 지정할 수 있다. 테이블을 생성할 때만 사용
- unique, nullable, insertable, updatable, columnDefinition, table : @Column의 속성과 같다.
@ManyToOne
다대일 관계에서 사용
- optional : false로 설정하면 연관된 엔티티가 항상 있어야 한다. 기본값 true
- fetch : 글로벌 페치 전략을 설정, @ManyToOne=FetchType.EAGER, @ManyToOne=FetchType.LAZY
- casacade : 연속성 전이 기능을 사용
- targetEntity : 연관된 엔티티의 타입 정보를 설정, 거의 사용하지 않음
연관관계 사용
JPA에서 엔티티를 저장할 때, 연관된 모든 엔티티는 영속 상태여야 한다.
조회
연관관계가 있는 엔티티를 조회하는 방법은 크게 2가지
- 객체 그래프 탐색
- 객체지향 쿼리 사용JPQL
연관관계 제거
1
member1.setTeam(null);
다음과 같이 대상 연관관계 객체를 null로 설정
연관된 엔티티 삭제
연관된 엔티티를 삭제하려면 기존에 있던 연관관계를 먼저 제거하고 삭제해야 한다. 그렇지 않으면 외래 키 제약조건으로 인해, 데이터베이스에서 오류가 발생
양방향 연관관계
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
@Entity
public class Member {
@Id
@Column(name = "MEMBER_ID")
private String id;
private String username;
@ManyToOne
@JoinColumn(name="TEAM_ID")
private Team team;
...
}
@Entity
public class Team {
@Id
@Column(name = "TEAM_ID")
private String id;
private String name;
@OneToMany(mappedBy = "team")
private List<Member> members = new ArrayList<Member>();
...
}
연관관계의 주인
엔티티를 양방향 연관관계로 설정하면 객체의 참조는 둘인데 외래 키는 하나다. 따라서 둘 사이에 차이가 발생
두 객체 연관관계 중 하나를 정해서 테이블의 외래키를 관리해야 하는데 이것을 연관관계의 주인 이라 한다.
양방향 매핑의 규칙: 연관관계의 주인
연관관계의 주인만이 데이터베이스 연관관계와 매핑되고 외래 키를 관리할 수 있다.
반면 주인이 아닌 쪽은 읽기만 할 수 있다.
- 주인은 mappedBy 속성을 사용하지 않는다.
- 주인이 아니면 mappedBy 속성을 사용해서 속성의 값으로 연관관계의 주인을 지정해야 한다.
연관관계의 주인을 정한다는 것은 사실 외래 키 관리자를 선택하는 것이다.
연관관계의 주인은 외래 키가 있는 곳
연관관계의 주인은 테이블에 외래 키가 있는 곳으로 정해야 한다.
양방향 연관관계 저장
양방향 연관관계의 주인이 외래 키를 관리한다. 따라서 주인이 아닌 곳에 입력된 값은 외래 키에 영향을 주지 않는다.
양방향 연관관계의 주의점
양방향 연관관계를 설정하고 가장 흔히 하는 실수는 연관관계의 주인에는 값을 입력하지 않고, 주인이 아닌 곳에만 값을 입력하는 것이다.
순수한 객체까지 고려한 양방향 연관관계
연관관계의 주인에만 값을 저장하고 주인이 아닌 곳에는 값을 저장하지 않아도 될까?
객체 관점에서 양쪽 방향에 모두 값을 입력해주는 것이 가장 안전하다.
연관관계 편의 메소드
1
2
3
4
5
6
7
8
public class Member {
private Team team;
public void setTeam(Team team) {
this.team = team;
team.getMembers().add(this);
}
}
이렇게 한 번에 양방향 관계를 설정하는 메소드를 연관관계 편의 메소드라 한다.
연관관계 편의 메소드 작성 시 주의사항
1
2
3
member1.setTeam(teamA);
member1.setTeam(teamB);
Member findMember = teamA.getMember(); // member1이 여전히 조회된다.
teamB로 변경할 때 teamA -> member1 관계를 제거하지 않았다.
1
2
3
4
5
6
7
8
9
public void setTeam(Team team) {
// 기존 팀과 관계를 제거
if (this.team != null) {
this.team.getMembers().remove(this);
}
this.team = team;
team.getMembers().add(this);
}
이 코드는 객체에서 서로 다른 단방향 연관관계 2개를 양방향인 것처럼 보이게 하려고 얼마나 많은 고민과 수고가 필요한지 보여준다.
정리하자면 객체에서 양방향 연관관계를 사용하려면 로직을 견고하게 작성해야 한다.