양방향 연관관계와 연관관계의 주인
@Entity
public class Team {
@Id @GeneratedValue
@Column(name = "TEAM_ID")
private Long id;
private String name;
@OneToMany(mappedBy = "team")
private List<Member> members = new ArrayList<>();
..Getter/Setter
}
저번시간에 한 Team에 List를 추가해줄 경우 양방향에서 접근을 할 수 있다. Team에서도 멤버 접근이 가능하고 멤버에서도 Team으로 접근이 가능해진다.
연관관계의 주인과 mappedBy
객체와 테이블간의 연관관계를 맺는 차이를 이해해야 한다.
객체와 테이블이 관계를 맺는 차이
- 위의 예제에서 객체 연관관계는 2개이다. 회원 -> 팀 연관관계 1개(단방향), 팀 -> 회원 연관관계 1개(단방향)
- 테이블 연관관계는 1개가 존재한다. 회원 <-> 팀의 연관관계 1개(양방향)
객체의 양방향 관계
이걸로 볼 때 객체의 양방향 관계는 사실 양방향 관계가 아니라 서로 다른 단방향 관계 2개이다. 객체를 양방향으로 참조하려면 단방향 연관관계 2개를 만들어야 한다.
테이블의 양방향 연관관계
테이블은 외래 키 하나로 두 테이블의 연관관계를 관리한다. 양쪽으로 조인 가능하다.
문제점
Team team을 변경할 때, Member에 있는 Team을 변경해야 할지 Team에 있는 List members를 변경해야 할지 혼란이 생긴다. 테이블의 Member는 TEAM_ID만 변경하면 되므로 테이블은 상관이 없다. 그래서 둘 중의 하나로 외래 키를 관리해야 한다. 그것이 연관관계 주인(Owner)이다.
연관관계의 주인(Owner)
양방향 매핑 규칙
- 객체의 두 관계중 하나를 연관관계의 주인으로 지정
- 연관관계의 주인만이 외래 키를 관리(등록, 수정)할 수 있다.
- 주인이 아닌쪽은 읽기만 가능하다.
- 주인은 mappedBy 속성을 사용하지 않는다.
- 주인이 아니면 mappedBy 속성으로 주인을 지정한다.
누구를 주인으로?
외래 키가 있는 곳을 주인으로 정하자. 외래키가 있는 곳이 DB에서 N : 1로 매칭이 된다. 즉 외래키가 있는 곳이 N인 것이다. 여기서는 Member.team이 연관관계의 주인이다. 진짜 매핑 = 연관관계의 주인, 가짜 매핑 = 주인의 반대편
Tip = 외래키가 있는 곳 즉, N인 곳을 주인으로 지정하자
주의할 점
Team team = new Team();
team.setName("TeamA");
em.persist(team);
Member member = new Member();
member.setName("member1");
//역방향(주인이 아닌 방향)만 연관관계 설정
team.getMembers().add(member);
em.persist(member);
이렇게 할 경우 TEAM_ID는 NULL이 된다. 그 이유는 team은 가짜 매핑이기 때문에 add 할 때 고려를 하지 않는다.
Team team = new Team();
team.setName("TeamA");
em.persist(team);
Member member = new Member();
member.setName("member1");
//연관관계의 주인에 값 설정
member.setTeam(team);
em.persist(member);
이렇게 진짜 매핑 member에 값을 추가 할 경우 정상적으로 수행된다.
Team team = new Team();
team.setName("TeamA");
em.persist(team);
Member member = new Member();
member.setName("member1");
team.getMembers().add(member);
member.setTeam(team);
em.persist(member);
Member findMember = em.find(Member.class, member.getId());
List<Member> members = findMember.getTeam().getMembers();
for (Member m: members) {
System.out.println("Members :" + m.getUsername());
}
tx.commit;
//team.getMembers().add(member)가 없을 경우 m은 출력이 안된다.
//commit하기 전까지 영속성 컨테이너에만 메모리 상태로 들어 있기 때문이다.
//그래서 team.getMembers().add(member)를 세팅해야 commit하기전 값을 알 수 있다.
//그러므로 team과 member 모두 값을 세팅해야한다.
양방향 연관관계일 경우 양쪽 모두 값을 세팅해야한다.
이것을 헷갈리지 않게 하기 위해 연관관계 편의 메소드를 생성하자.
public void setTeam(Team team){ //changeTeam 같이 이름도 중요하게 변경
this.team = team;
team.getMembers().add(this);//연관관계 편의 메소드
}
양방향 매핑 시에 무한 루프가 발생할 수 있다.
toString() 일 경우 Member에서 team을 출력, team에서 members를 출력하므로 무한 루프가 발생한다.
되도록이면 toString()과 lombock은 사용하지 않는다. JSON 생성 라이브러리는 컨트롤러에서 ENTITY를 반환하지 않는다.
양방향 매핑 정리
- 단방향 매핑만으로도 이미 연관관계 매핑은 완료할 수 있다.
- 양방향 매핑은 반대 뱡향으로 조회 기능이 추가된 것 뿐이다.
- JPQL에서 역방향으로 탐색할 일이 많아진다.
- 단방향 매핑을 잘 하고 양방향은 필요할 때 추가해도 된다, 테이블에 영향을 주지 않는다.
강의 출처 : https://www.inflearn.com/course/ORM-JPA-Basic
자바 ORM 표준 JPA 프로그래밍 - 기본편 - 인프런 | 강의
JPA를 처음 접하거나, 실무에서 JPA를 사용하지만 기본 이론이 부족하신 분들이 JPA의 기본 이론을 탄탄하게 학습해서 초보자도 실무에서 자신있게 JPA를 사용할 수 있습니다., - 강의 소개 | 인프런
www.inflearn.com
'BackEnd > 자바 ORM 표준 JPA 프로그래밍' 카테고리의 다른 글
JPA 다양한 연관관계 매핑 (0) | 2022.12.26 |
---|---|
JPA 실전 예제2 - 연관관계 매핑 시작 (0) | 2022.12.23 |
JPA 단방향 연관관계 (0) | 2022.12.23 |
JPA 실전 예제 1 - 요구사항 분석과 기본 매핑 (0) | 2022.12.23 |
JPA 엔티티 매핑(필드와 컬럼 매핑, 기본 키 매핑) (1) | 2022.12.22 |