연관관계란 두 개의 데이터가 무언가를 통해 관계를 맺고 있다는 걸 말한다.
DB 테이블의 경우 FK를 통해 연관관계를 맺고, 객체의 경우 레퍼런스를 통해 관계를 맺는데 JPA는 이 두개의 페러다임의 차이를 엮어준다.
엔티티 객체는 연관관계 방향이 존재하는데 A객체가 B객체를 일방적으로 참조하면 단방향 참조, B객체도 A객체를 참조하면 양방향 참조라고 한다.
엔티티 객체간의 관계는 일대일, 다대일, 일대다, 다대다가 존재한다.
엔티티 객체간에 참조를 통해 연관관계를 맺을 때 어떤 객체의 참조값을 기준으로 삼고 관리할지 정해야하는데 이것을 연관관계 주인이라고 한다.
연관관계는 데이터 중심의 연관관계와 객체 중심의 연관관계가 있는데 JPA는 객체 중심의 연관관계를 사용하여 데이터 중심의 연관관계인 DB에 접근하도록 돕는다.
데이터 중심의 연관관계는 보통 테이블에 맞춘 연관관계를 말한다. 때문에 Member객체와 Team객체의 연관관계를 teamId로 맺어주게 된다. 이 방식을 사용할 때 Member가 Team정보에 접근하는 법은 다음과 같다.
// 멤버 조회
Member findMember = em.find(Member.class, member.getId());
// 팀 조회
Team findTeam = em.find(Team.class, findMember.getTeamId());
객체 중심의 연관관계는 참조를 통해서 이루어 지는데 Member객체에 Team객체를 참조할 수 있도록 한다. 이 방식을 사용할 때 Member에서 Team정보에 접근하는 방법은 다음과 같다.
//멤버 조회
Member findMember = em.find(Member.class, member.getId());
//참조를 사용해서 팀 조회
Team findTeam = findMember.getTeam();
위 객체중심의 연관관계는 Member객체가 Team객체를 참조하는데 둘의 관계는 N:1 즉, 다대일 관계다.
연관관계를 통한 객체 접근방식은 단방향과 양방향 두가지가 있는데 권장사항은 최대한 단방향으로 설계를 고려하고 추후 필요하다면 양방향 관계로 만들어주는 것이다.
단방향 관계는 한 쪽에서만 접근이 가능하도록 하는 것을 말한다.
@Entity
public class Member {
@Id @GeneratedValue
private Long id;
...
@ManyToOne
@JoinColumn(name = "TEAM_ID") // 엔티티명_엔티티 키 조합명.
private Team team
...
}
// 단방향 객체 접근
member.getTeam() // 가능
team.getMembers() // 불가능 - 정의 하지 않았기 때문
Member 엔티티는 위와 같이 Team 엔티티를 참조하게 되는데 참조하는 객체에 @ManyToOne과 같이 어떤 방식의 관계를 가지는지 어떤 키 값으로 참조를 맺는지 명시해줘야 하며, 이렇게 참조된 필드를 연관관계 주인이라고 한다.
보통 다대일 관계에서는 다 쪽을 연관관계 주인으로 정하고 관계를 맺으며, 다 쪽에서 일 방향으로 접근이 가능하도록 한다.
연관관계 주인인 참조 객체의 수정이 가능하다.
@Entity
public class Team {
@Id @GeneratedValue
private Long id;
...
//연관관계 주인 엔티티의 참조변수명을 mappedBy에 명시
// 예시의 경우 Member에서 team변수를 연관관계 주인으로 사용중.
@OneToMany(mappedBy = "team")
List<Member> members = new ArrayList<Member>();
…
}
// 양방향 관계 추가
member.getTeam() // 가능
team.getMembers() // 가능
개발하다보면 일 쪽에서도 다 쪽으로 접근해야할 경우도 생긴다. 이럴 때 위와 같이 일쪽에서도 참조가 가능하도록 할 수 있다.
mappedBy로 참조한 객체는 읽기만 가능하고 변경은 불가능하다.
연관관계 주인 설정 JoinColumn과 mappedBy가 상당히 애매한데, 연습이 많이 필요 할 것 같다.
연관관계의 주인은 JoinColumn에서
엔티티명_엔티티 키
조합으로 명시해주고 연관관계 지배(?)를 받는 곳은 mappedBy에서연관관계 주인으로 사용 중인 변수명
을 넣어준다.
엔티티 객체의 연관관계는 다대일, 일대일, 일대다, 다대다가 있고 지원하는 어노테이션은 @ManyToOne, @OneToOne, @OneToMany, @ManyToMany이다.
권장사항은 항상 다 쪽을 연관관계 주인으로 설정하고 사용하는 것이며 이경우 다대일과, 일대다의 차이가 없다. 또한 일대일 관계는 어느 쪽을 연관관계 주인으로 설정해도 똑같으니 주인 테이블을 기준으로 삼는 것을 추천한다.
다대다의 경우는 정말로 안 쓰는걸 권장한다.