반응형
다대일 페치 조인 (Member - Team)
다대일 페치 조인을 하게 되면 데이터 중복이 발생하지 않습니다.
String jpql = "select m from Member m join fetch m.team";
List<Member> members = em.createQuery(jpql, Member.class)
.getResultList();
for (Member member : members) {
//페치 조인으로 회원과 팀을 함께 조회해서 지연 로딩X
System.out.println("username = " + member.getUsername() + ", " +
"teamName = " + member.getTeam().name());
}
// username = 회원1, teamname = 팀A
// username = 회원2, teamname = 팀A
// username = 회원3, teamname = 팀B
하지만,
일대다 관계, 즉 컬렉션 페치조인을 할 경우 문제가 발생합니다.
select t
from Team t join fetch t.members
where t.name = ‘팀A'
일대다 페치 조인 시 데이터 뻥튀기, 즉 데이터 중복 문제가 발생합니다.
Team에 A는 1개인데 Member(N측)와 조인하면서 데이터(TeamA)가 늘어어납니다.
애플리케이션 측면에서는
- 첫 번째 행에 값을 “팀 A”를 영속성 컨텍스트에 올립니다.
- 두 번째 행의 동일한 “팀 A”를 영속성 컨텍스트에서 같은 객체를 사용합니다.
String jpql = "select t from Team t join fetch t.members where t.name = '팀A'"
List<Team> teams = em.createQuery(jpql, Team.class).getResultList();
for(Team team : teams) {
System.out.println("teamname = " + team.getName() + ", team = " + team);
for (Member member : team.getMembers()) {
//페치 조인으로 팀과 회원을 함께 조회해서 지연 로딩 발생 안함
System.out.println(“-> username = " + member.getUsername()+ ", member = " + member);
}
}
// teamname = 팀A, team = Team@0x100
// -> username = 회원1, member = Member@0x200
// -> username = 회원2, member = Member@0x300
// teamname = 팀A, team = Team@0x100
// -> username = 회원1, member = Member@0x200
// -> username = 회원2, member = Member@0x300
일대다 페치 조인으로 뻥튀기된 Team 엔티티로 같은 데이터인 “팀A” 엔티티가 2번 조회됩니다.
이를 해결하기 위해 DISTINCT 키워드를 사용합니다.
페치 조인과 DISTINCT
- JPQL의 DISTINCT 2가지 기능 제공
-
- SQL에 DISTINCT를 추가
- 애플리케이션에서 엔티티 중복 제거
-
(1) SQL에 DISTINCT를 추가하지만 데이터가 다르므로 SQL 결과에서 중복제거 실패
(2) DISTINCT 추가로 애플리케이션에서 중복 제거시도
- 같은 식별자를 가진 Team 엔티티 제거
String jpql = "select distinct t from Team t join fetch t.members where t.name = '팀A'"
List<Team> teams = em.createQuery(jpql, Team.class).getResultList();
for(Team team : teams) {
System.out.println("teamname = " + team.getName() + ", team = " + team);
for (Member member : team.getMembers()) {
//페치 조인으로 팀과 회원을 함께 조회해서 지연 로딩 발생 안함
System.out.println(“-> username = " + member.getUsername()+ ", member = " + member);
}
}
// [DISTINCT 추가시 결과]
// teamname = 팀A, team = Team@0x100
// -> username = 회원1, member = Member@0x200
// -> username = 회원2, member = Member@0x300
반응형
'Spring Framework > JPA' 카테고리의 다른 글
[JPA] JPA 영속성 전이란, CASCADE.ALL 사용법, 연관관계 편의 메소드란 (0) | 2023.12.08 |
---|---|
[JPA] JPA 상속 관계 매핑과 @MappedSuperClass 사용하기 - 조인전략, 싱글 테이블, @CreatedDate, @LastModifiedDate (1) | 2023.11.28 |
[JPA] 연관관계 매핑, 양방향, 연관관계 주인, mappedBy (0) | 2023.11.11 |
[JPA] Spring JPA 프록시 객체와 지연로딩이란 무엇인가, 사용하는 이유 (1) | 2023.10.09 |
[JPA] Spring-JPA 엔티티 값 Update 하기 - save(), 더티체킹(Dirty Checking - set()) (0) | 2023.09.15 |