JPA(Java Persistence API) 05 - 연관관계(사용법)

연관관계 저장

JPA에서 엔티티를 저장할 때 연관된 모든 엔티티는 영속 상태여야 한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
final Team team1 = new Team();
team1.setTeamName("team1");
team1.setTeamType(TeamType.A);
entityManager.persist(team1);

final Member member1 = new Member();
member1.setUsername("member1");
member1.setAge(20);
member1.setTeam(team1); // Member와 Team 간 연관관계 저장
entityManager.persist(member1);

final Member member2 = new Member();
member2.setUsername("member2");
member2.setAge(10);
member2.setTeam(team1); // Member와 Team 간 연관관계 저장
entityManager.persist(member2);

양방향 연관관계 저장

양방향 연관관계 저장 시 주의할 점으로는 연관관계의 주인이 아닌 엔티티에서 연관관계를 저장해도 실제로 데이터베이스에 저장되지 않는다는 점이다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
final Member member1 = new Member();
member1.setUsername("member1");
member1.setAge(20);
// member1.setTeam(team1);
entityManager.persist(member1);

final Member member2 = new Member();
member2.setUsername("member2");
member2.setAge(10);
// member2.setTeam(team1);
entityManager.persist(member2);

final Team team1 = new Team();
team1.setTeamName("team1");
team1.setTeamType(TeamType.A);
// 연관관계의 주인이 아닌 쪽에서 아래와 같이 연관관계를 저장해도 데이터베이스에는 저장되지 않는다.
team1.setMembers(Lists.newArrayList(member1, member2));
entityManager.persist(team1);

그렇다고 연관관계의 주인 쪽에만 객체를 저장하지는 말고, 양쪽 방향 모두 객체를 저장해주는 것이 가장 안전하다.

아래와 같이 연관관계 저장 시 연관관계의 주인이 아닌 쪽에도 관계가 설정되도록 기능을 확장할 수 있다. 아래 예제 코드를 참조할 것.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Entity
@Table(name = "member")
public class Member {

// ...

public void setTeam(Team team) {
if (this.team != null) {
// 기존에 설정된 연관관계는 아래와 같이 제거해야 한다.
this.team.getMembers().remove(this);
}
this.team = team;
// 연관관계의 주인이 아닌 쪽에도 아래와 같이 관계를 설정해주는 편이 안전하다.
team.getMembers().add(this);
}

// ...
}

연관관계 조회

객체 그래프 탐색

객체 연관관계를 사용한 객체 그래프 탐색으로 연관관계 엔티티를 조회할 수 있다.

1
2
final Member member = entityManager.find(Member.class, member1.getId());
final Team team = member.getTeam();

JPQL 사용

객체지향 쿼리인 JPQL의 조인을 사용하여 연관관계 엔티티를 조회할 수 있다.

1
2
3
4
5
6
7
final String jpql = "select m from Member m join m.team t where t.teamName=:teamName";
final List<Member> resultList = entityManager.createQuery(jpql, Member.class)
.setParameter("teamName", team1.getTeamName())
.getResultList();
for (final Member member : resultList) {
System.out.println("member's username=" + member.getUsername());
}

연관관계 수정

엔티티의 연관관계 엔티티만 변경해두면 트랜잭션 커밋 시 변경 감지 기능이 작동하여 연관관계가 수정된다.

1
2
3
4
5
6
7
8
9
final Team team2 = new Team();
team2.setTeamName("team2");
team2.setTeamType(TeamType.B);
entityManager.persist(team2);

findMember1.setTeam(team2);

final Member findMember2 = entityManager.find(Member.class, member.getId());
System.out.println(findMember2.getTeam().getTeamName());

연관관계 제거

연관관계를 null로 설정하면 연관관계가 제거된다.

1
2
final Member findMember = entityManager.find(Member.class, member.getId());
findMember.setTeam(null);

연관된 엔티티를 삭제할 때에는 기존의 연관관계 먼저 제거하고 삭제할 것. 아래 코드는 외래 키 제약조건으로 인해 오류가 발생한다.

1
2
3
4
5
6
7
member1.setTeam(team1);
member2.setTeam(team2);

member1.setTeam(null);
// member2.setTeam(null);

entityManager.remove(team1);

위 내용은 김영한님의 자바 ORM 표준 JPA 프로그래밍를 읽으며 개인적으로 요약 및 정리하는 내용이다.
자세한 내용이 알고 싶으면 김영한님의 자바 ORM 표준 JPA 프로그래밍을 직접 읽어보길 추천한다.