양뱡향 매핑 시 가장 많이 하는 실수
양방향 관계에서 가능 많이 하는 실수는 연관관계의 주인 값을 입력하지 않고 진행하는 것이다.
Team team = new Team();
team.setName("TeamA");
em.persist(team);
Member member = new Member();
member.setName("member1");
// 역방향(주인이 안니 방향)만 연관관계 설정
team.getMembers().add(member);
em.pesist(member);
이런 경우 연관관계의 주인인 member의 team값은 null이 저장이 된다. 양방향의 주인은 외래키를 관리자 역할을 하므로 Member.team 값을 입력해 주어야 정상적으로 값이 매핑이 되게 된다.
순수한 객체를 고려
그렇다면 연관관계 주인값만 정상적으로 넣어주면 아무상관 없는 것일까? 객체의 입장에서 생각해 보면 양쪽 방향 모두 값을 입력해주는 것이 가장 안정하다. 양쪽 다 넣어주지 않으면 JPA를 사용하지 않은 순수한 객체에서 문제가 발생할 수 있다.
사실 JPA에서 flush, clear 를 하고 find 하여 값을 가져온다면 영속성 컨테스트의 1차 캐시가 비워지므로 다시 DB를 호출하여 값을 가져오는데 문제가 없다.
Team team = new Team();
team.setName("teamA");
em.pesist(team);
Member member = new Member();
member.setUsername("member1");
member.setTeam("team");
em.pesist(member);
// flush, clear
em.flush();
em.clear();
Team findTeam = em.find(Team.class, team.getId());
// 값이 존재한다.
List<Member> members = findTeam.getMembers();
하지만 flush, clear를 하지 않는다면 findTeam은 처음 저장한 Team의 값이 1차 캐시로 남아있어 member 컬렉션 값이 존재하지 않는다. 그렇기 때문에 아래와 같이 양쪽 매핑 모두 값이 지정하는 방식으로 해야 된다.
Team team = new Team();
team.setName("teamA");
em.pesist(team);
Member member = new Member();
member.setUsername("member1");
member.setTeam("team");
em.pesist(member);
// 주인이 아닌 쪽도 값을 매핑해준다.
team.getMembers().add(member);
Team findTeam = em.find(Team.class, team.getId());
// 값이 존재한다.
List<Member> members = findTeam.getMembers();
연관관계 편의 메서드
결국 양방향은 양쪽 엔티티 모두 신경써줘야 한다. 하지만 위 예제처럼 각각 (set, add) 호출하게 된다면 실수를 유발할 위험성이 발생한다. 그럼 사실 문제가 발생하여도 원인을 찾기가 힘들어 큰 문제를 야기할 수 있다.
member.setTeam(team);
team.getMemebers().add(member);
양방향 관계에서는 위 코드를 한 코드처럼 사용하는 것을 권장한다.
// 어노테이션은 생략
public class Member{
private Team team;
public void changeTeam(Team team) {
this.team = team;
team.getMembers().add(this);
}
}
위와 같이 메소드 하나로 양방향 관계 모두 설정하도록 변경하여 사용한다. 메서드 명은 setter 대신 다른 메서드 명을 사용하는 것이 좋다. setter, getter는 아무래도 자바 기본 관례 같은 메서드이기 때문에 로직이 추가된다면 명칭을 다르게 해주는 것을 선호한다.
주의할 점은 아무래도 List<Member> 는 컬렉션이기 때문에 연속적으로 추가되는 로직이 있다면 문제가 발생할 수 있다. 그런 경우는 아래와 같이 null 체크와 기존 컬렉션을 비교해 제거해 주는 로직을 추가하면 된다.
// 어노테이션은 생략
public class Member{
private Team team;
public void changeTeam(Team team) {
// 기존 팀과 관계를 제거
if(team != null) {
this.team.getMembers().remove(this);
}
this.team = team;
team.getMembers().add(this);
}
}
참고
김영한님의 인프런 강의와 책으로 참조하였습니다.
https://www.inflearn.com/course/ORM-JPA-Basic
자바 ORM 표준 JPA 프로그래밍 - 기본편 | 김영한 - 인프런
김영한 | JPA를 처음 접하거나, 실무에서 JPA를 사용하지만 기본 이론이 부족하신 분들이 JPA의 기본 이론을 탄탄하게 학습해서 초보자도 실무에서 자신있게 JPA를 사용할 수 있습니다., 실무에서도
www.inflearn.com
https://www.yes24.com/Product/Goods/19040233
자바 ORM 표준 JPA 프로그래밍 - 예스24
자바 ORM 표준 JPA는 SQL 작성 없이 객체를 데이터베이스에 직접 저장할 수 있게 도와주고, 객체와 관계형 데이터베이스의 차이도 중간에서 해결해준다. 이 책은 JPA 기초 이론과 핵심 원리, 그리고
www.yes24.com
'Framework > JPA' 카테고리의 다른 글
[JPA] 양방향 연관관계와 연관관계의 주인(with mappedBy) (0) | 2024.04.26 |
---|---|
[JPA] 객체 관계 매핑에 필요한 @JoinColum 속성 알아보기 (0) | 2024.04.26 |
[연관관계 매핑 기초] 단방향 연관관계와 객체와 테이블의 차이 (0) | 2024.03.21 |
[JPA] 엔티티 매핑 - 기본 키 매핑 (0) | 2024.03.15 |
[JPA] 엔티티 매핑 - 필드와 컬럼 매핑 (0) | 2024.03.14 |