양방향 연관관와 연관관계의 주인
양방향 매핑을 보면 테이블을 단방향 매핑과 사진이 동일한데 객체는 Team에 members 라는 리스트가 추가 되었다.
테이블은 외래키 하나로 Member테이블에서 TEAM_ID와 Team테이블의 TEAM_ID를 조인해서 Member테이블에서든 Team테이블에서든 서로의 테이블을 조회할 수 있는데 객체는 Team 객체에 필드를 추가해 줘야한다.
객체의 양방향 관계는 사살 양방향 관계가 아니라 서로 다른 단방향 관계 2개다.
- Member 객체에 Team필드
- Team 객체에 List members 필드
객체 연관관계를 단방향에서 양방향으로 바꿔보자
@Entity
public class Team(
@Id @GeneratedValue
private Long id;
private String name;
@OneToMany(mappedBy="team")
private List<member> members = new ArrayList<>(); //add 할 때 null이 되지 않도록 미리 초기화 해 놓는게 관례이다.
}
- private List<member> members: Team 객체에 팀에 들어가 있는 멤버를 담을 리스트 필드 추가
- @OneToMany: Team 객체 입장에서 Member와의 관계가 OneToMany이다.
- mappedBy="team": Member의 team필드한테 매핑되어 있다.
Member findMember = em.find(Member.class, member.getId());
List<Member> members = findMember.getTeam().getMembers();
member에서 team을 찾아서 team에서 member를 찾을 수 있다.
양방향의 딜레마 문제
- Member에서 team값을 바뀌었을 때 Member의 외래키 값이 바뀌어야하나?
- Team의 members의 값이 바뀌었을 때 Member의 외래키 값이 바뀌어야하나?
둘 중 하나로 외래키를 관리해야한다
→ 연관관계 주인이 관리해야함
연관관계 주인
연관관계주인 정하는 법: 외래키가 있는 곳을 주인으로 정해라
사진에서 Member 테이블에 외래키가 있으므로 Member 객체의 team을 연관관계 주인으로 지정
Member객체의 team이 연관관계 주인이다.
→ @JoinColumn으로 지정해 두었다.
Team의 members는 Member의 team에 의해 관리 된다고 해두었다. members에 값을 넣어봐야 아무런 테이블에 변화가 없다. 대신 조회는 가능
→ mappedBy="team"
Q. 외래키가 없는 엔티티를 주인으로 설정하면?
A. Team 엔티티에 주인이 있으면 Team의 members의 값을 변경했는데 데이터베이스에서는 Member 테이블 값이 변경되는 상황이 발생 → 혼란 유발
양방향 매핑 규칙
- 객체의 두 관계중 하나를 연관관계의 주인으로 지정
- 연관관계의 주인만이 외래 키를 과린(등록, 수정)
- 주인이 아닌쪽은 읽기만 가능
- 주인은 mappedBy 속성 사용 X
- 주인이 아니면 mappedBy 속성을 주인으로 지정
양방향 매핑 주의점
양방향 매핑시 가장 많이 하는 실수: 연관관계의 주인에 값을 입력하지 않음.
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 team = new Team();
team.setName("TemaA");
em.persist(team);
Member member = new Member();
member.setUsername("memeber1");
//연관관계 주인에서 team 데이터 입력
member.setTeam(team);
em.persist(member);
em.flush();
em.clear();
하지만 team.getMembers().add(member)를 하지 않으면 2군데에서 문제발생
1. 1차 캐시에서 조회 시 데이터가 없다.
- 위 코드처럼 em.fush(), em.clear()를 하면 상관없는데 만약에 안하면 em.find(Team.class, team.getId());를 하면 1차 캐시에서 데이터를 가져올 것인데 현재 Team의 상태는 getMembers에 아무것도 없는 상태로 1차 캐시에 있어서 em.find를 해도 데이터가 없어서 select 쿼리가 나가지 않는다.
Team findTeam = em.find(Team.class, team.getId()); //select 쿼리가 나가지 않는다
List<Member> members = findTeam.getMembers();
2. test 케이스 작성할 때 jpa 없이 순순 자바코드로 작성할 경우
- jpa 없이 순수 자바코드로 작성할 때 team에도 값을 넣어주지 않으면 데이터가 없다.
결론은 양방향 연관관계에서는 양쪽에 다 값을 세팅해 줘야한다.
방법 1. Member 엔티티에서 team값을 세팅해줄 때 Team 엔티티의 member값도 세팅해준다.
setTeam 메소드 이용
public void setTeam(Team team){
this.team = team;
team.getMembers().add(this); //this = Member
}
Member 엔티티에 team.getMember().add(this) 추가해줘서 애플리케이션 로직에서는 team.getMember().add(member)를 따로 작성하지 않아도 된다.
방법 2. Team 엔티티의 setMember 메소드를 이용해서 세팅
addMember 메소드 이용
public void addMember(Member member) {
member.setTeam(this);
members.add(member);
}
양방향 매핑 정리
객체와 테이블 매핑하는 것은 단방향으로 이미 완료
단방향 매핑하고 양방향을 필요할 때 추가
- mappedBy 코드만 추가하면 돼서 테이블에 영향을 주지 않는다.
'JPA' 카테고리의 다른 글
프록시 (1) | 2023.11.09 |
---|---|
고급 매핑 (0) | 2023.11.09 |
연관관계 매핑 기초 (0) | 2023.11.02 |
엔티티 매핑 (1) | 2023.10.29 |
영속성 관리 - JPA 내부 동작 방식 (0) | 2023.10.28 |