반응형
- 양방향 매핑 시 가장 많이 하는 실수
//저장
Member member = new Member();
member.setUsername("member1");
em.persist(member);
Team team = new Team();
team.setName("TeamA");
team.getMembers().add(member);
em.persist(team);
em.flush();
em.clear();
tx.commit();
- 이렇게 코드를 바꾸고 실행을 해 본다.
- TEAM_ID가 null이 뜬다.
//저장
Team team = new Team();
team.setName("TeamA");
//team.getMembers().add(member); //<- 중요 포인트
em.persist(team);
Member member = new Member();
member.setUsername("member1");
member.setTeam(team); // <- 중요 포인트
em.persist(member);
em.flush();
em.clear();
tx.commit();
- 이렇게 바꿔서 코드를 실행해 본다.
- 연관관계의 주인에만 값을 넣고 연관관계의 주인이 아닌 곳에는 값을 뺴 본다.
- 이제 TEAM_ID에 1이 들어갔다.
- 양방향 매핑 시 가장 많이 하는 실수는 위와 같이 연관관계의 주인에 값을 넣지 않고 다른 곳에 값을 넣는 경우가 잦은 점이다.
- 하지만, 양방향 매핑 시 가능하면 양쪽에 값을 다 넣어주는 게 좋다.
team.getMembers().add(member);
- 둘 다 안 써줘도, 위 코드를 주석처리하고 돌려도 정상으로 돌아간다.
- 하지만 문제가 있는데 em.flush() em.clear()를 하면 문제가 없다. 하지만 flush와 clear를 안 하면 1차 캐시에서 조회된 것이 그대로 튀어나온다.
Team findTeam = em.find(Team.class, team.getId());
List<Member> members = findTeam.getMembers();
for (Member m : members) {
System.out.println("m.getUsername() = " + m.getUsername());
}
- 코드로 확인을 해보면 select 쿼리가 안 나간다.
- 때문에 객체 지향적으로 생각했을 때 양쪽에 다 값을 넣어주는 게 맞다.
- 특히 테스트 케이스를 만들 때 양쪽을 사용해야 하기 때문에 양방향 연관관계를 셋팅할 때는 양쪽에 다 값을 셋팅해 줘야 하는 게 맞다.
- 순수 객체 상태를 고려한다.
- 연관관계 양방향 설정을 깜빡할 수 있기도 하고 편리하게 사용하기 위해서 연관관계 편의 메소드를 생성하는 게 좋다.
- Member.class에서 설정한 부분을
public void setTeam(Team team) { this.team = team; team.getMembers().add(this); }
- 이렇게 바꿔준다.
//저장 Team team = new Team(); team.setName("TeamA"); em.persist(team); Member member = new Member(); member.setUsername("member1"); member.setTeam(team); //<- 중요 포인트 em.persist(member); //team.getMembers().add(member); // <- 중요 포인트
- 결과적으로 이렇게 setTeam을 호출해도 양쪽으로 값이 걸린다.
- 추가 팁으로 이때, 메소드 명에 set을 사용하면 setter 관례에 따라 사용한 게 되므로 이 경우 메소드 명을 바꿔준다고 한다.
public void changeTeam(Team team) { this.team = team; team.getMembers().add(this); }
- 이렇게 setTeam → changeTeam으로 chage를 주로 넣는다고 한다.
- 로직이 들어가서 chage 처럼 이름을 바꿔 넣으면 뭔가 중요하게 뭔가를 하는구나를 알 수 있어 권장한다고 한다.
- 정식으로 사용하기 위해서는 더 복잡하다.
- Null 체크 등..
- 하지만 그렇게 깊게 사용할 일이 자주 없어서 평소에는 이렇게 간단하게 편의 메소드를 사용하면 된다.
- 더 깊은 내용은 너무 복잡해서 JPA 책을 참고하면 된다. 보통 실무에서 이정도 사용해도 무리는 없다.
public void addMember(Member member) { member.setTeam(this); members.add(member); }
- 연관관계 편의 메소드를 반대로 할 수 도 있다.
- 기준이 반대, Team 기준, Member 기준 이렇게 양쪽 다 사용이 가능하다.
- 주의 점은 아래서 다룬다.
- 기준이 반대, Team 기준, Member 기준 이렇게 양쪽 다 사용이 가능하다.
team.addMember(member);
- 이것을 대신 넣어 사용할 수 있다.
- 대신 양쪽 다 연관관계 편의 메소드를 설정할 시 문제가 되므로 메소드 하나를 설정했으면 반대쪽 메소드는 삭제해야 한다.
- 양방향 매핑 시 무한 루프도 조심해야 한다.
- toString(), lombok, JSON 생성 라이브러리를 조심해야 한다.
- 양쪽으로 toString()을 무한 생성하게 될 수도 있다.
- StackOverflowError가 뜰 수 있다.
- 양쪽으로 toString()을 무한 생성하게 될 수도 있다.
- lombok에서는 toString()을 만들지 말자.
- JSON 생성 라이브러리를 잘 알고 사용하면 된다.
- Controller에는 Entity를 절대 반환하면 안 된다.
- JSON으로 주로 통신해서 좋다고 사용하는데 무한루프가 생길 수 있고 Entity를 API에 반환해 버리면 Entity를 변경하는 순간 API 스펙이 바껴버린다. 때문에 문제가 생길 수 있다.
- Entity를 DTO로 변환해서 반환하는 것을 추천한다. 그렇게 되면 JSON 생성 라이브러리를 통해 오류가 생기는 경우가 줄어들 것이다.
- toString(), lombok, JSON 생성 라이브러리를 조심해야 한다.
- public void setTeam(Team team) { this.team = team; }
- 단방향 매핑으로도 연관관계 매핑은 완료다.
- 처음 설계에서는 단방향 매핑으로 끝내야한다.
- 연관 테이블이 매우 많아도 JPA 사용하고 설계에 들어가면 일단 단방향 매핑으로 설계를 완료해야 한다.
- 객체만으로 설계를 할 수는 없다. 테이블 설계와 동시에 들어가야 하는데 테이블 관계에 대한 FK가 어느 정도 구상되면 단방향으로 끝내야 한다.
- 단방향을 잘 하고 양방향은 필요에 따라 추가하면 된다.
- 비즈니스 로직을 기준으로 연관관계의 주인을 선택하지 말고 외래키를 기준으로 해야한다.
- 성능이나 운영 관점에서 볼 때 이렇게 하는 게 이점이 많다. 그리고 헷갈릴 게 없다.
- 외래키가 들어간다? → 너 Owner 이렇게 간단하게 정해주면 된다.
- 양방향 연관관계가 형성되면?
- 연관관계 편의 메소드를 사용한다.
자바 ORM 표준 JPA 프로그래밍 - 기본편을 참고하여 공부하였습니다.
반응형
'Data Base > JPA' 카테고리의 다른 글
[JPA] : 다대일, N:1 (0) | 2022.04.11 |
---|---|
[JPA] : 연관관계 매핑 시작하기 (0) | 2022.04.09 |
[JPA] : 양방향 연관관계, 연관관계 주인(Owner) (0) | 2022.04.08 |
[JPA] : 연관관계 매핑 기초 (0) | 2022.04.06 |
[JPA] : 기본 매핑하기 (0) | 2022.04.06 |
댓글