@Entity
public class Member {
@Id @GeneratedValue
private Long id;
@Column(name = "TEAM_ID")
private Long teamId; // 참조 대신 외래키를 그대로 사용
...
}
//Main.java
...
Member findMember = em.find(Member.class, member.getId());
// 바로 참조할 수 있는 연관관계가 없다.
Team findTeam = em.find(Team.class, findMember.getTeamId());
// Team findTeam = findMember.getTeam(); 이 더 객체지향적인 방법이다.
@Entity
public class Member {
@Id @GeneratedValue
private Long id;
@ManyToOne
@JoinColumn(name = "TEAM_ID")
private Team team; // 외래키 대신 참조를 사용
...
}
// 조회
Member findMember = em.find(Member.class, member.getId());
// 참조를 사용한 연관관계 조회
Team findTeam = findMember.getTeam();
mappedBy
속성을 줌으로써 주인을 표시해야 한다.@Entity
public class Member {
@Id @GeneratedValue
private Long id;
@ManyToOne
@JoinColumn(name = "TEAM_ID")
private Team team; // 외래키 대신 참조를 사용
...
}
@Entity
public class Team {
@Id @GeneratedValue
private Long id;
@OneToMany(mappedBy = "team")
List<Member> members = new ArrayList<>(); // 컬렉션 선언과 초기화를 함께 해주는 것은 JPA 관례이다.
...
}
Team team = new Team();
team.setName("TeamA");
em.persist(team);
Member member = new Member();
member.setName("member1");
member.setTeam(team);
em.persist(member);
// 여기서 team의 members를 조회하면 아무것도 안나온다.
// 현재 team은 1차 캐시의 상태이다.
// 만약 캐시를 비우고 나서 team.getMembers를 한다면, <Select:Team>, <Select:Member> 쿼리가 실행되면서 정상적으로 출력이 될 것이다.
// 하지만 캐시를 비우지 않은 순수 객체 상태에서는 정상적으로 역방향 참조를 할 수 없다.
for (Member member : team.getMembers()) {
System.out.println(member.getId());
}
연관관계 편의 메소드
란 단방향 관계를 맺을 때 한번에 양방향 관계를 맺을 수 있도록 하나의 함수에 로직을 추가하는 것이다.// Member.java
...
public void changeTeam(Team team) {
this.team = team;
team.getMembers.add(team);
// 수정 시 로직 추가 요구됨
...
JPA로 객체간의 연관관계를 매핑하는 방법은 크게 외래키로 간접참조하는 방법이 있고, 객체를 직접 참조하는 방법이 있다. 객체를 직접 참조하게 되면 연관관계 객체의 내부를 별도의 쿼리조회없이 접근할 수 있기 때문에 보다 객체지향적인 프로그래밍이 가능하다.
그러나 모든 연관관계에서 직접 참조를 하게 될 경우, 연관관계 객체를 의도치 않게 변경시킬 위험이 있기 때문에 같은 도메인의 어그리게잇에 한해서만 사용하는 것이 좋을 듯 하다.
직접 참조를 하더라도 단방향 매핑과, 양방향 매핑이 있다. 기본적으로 초기 설계는 모두 단방향으로 완료할 수 있어야하며 양방향은 꼭 필요한 경우에만 매핑을 허용하는 것이 좋다.
양방향 매핑이 불가피한 경우라면 설계를 다시 고민해보거나, 값을 빠지는 경우나 무한루프를 주의하면서 사용해야한다.