@Entity
public class Member {
@Id
@GeneratedValue
@Column(name = "member_id")
private Long id;
private String name;
@JsonIgnore
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "team_id")
private Team team;
}
@Getter
@Entity
public class Team {
@Id
@GeneratedValue
@Column(name = "team_id")
private Long id;
private String name;
@OneToMany(mappedBy = "team")
private List<Member> members;
}
Team에서 일대다로 묶여있고 Member가 있다고 하자.
만약 요청하는 클라이언트가 각 Team별로 회원 이름을 요청할 경우 어떻게 줘야할까?
// Repository
@Transactional
public List<Team> findAllTeam() {
return em.createQuery("select t from Team t join fetch t.members", Team.class)
.getResultList();
}
Repository에서는 join fetch를 사용하여 팀과 회원을 가져오게 된다.
하지만 여기서 가져오는 것은 이렇다.
데이터가 중복되어 가져오게 된다.
Hibernate 5.6.14
스프링 부트 2.7.6 버전으로 확인하면 Hibernate 5.6.14 버전을 가져온다.
이 버전으로 findAllTeam을 실행하여 출력하면 다음과 같이 나온다.
DB에서 본 것처럼 중복된 데이터가 나온다. 만약 그대로 응답할 경우 중복값을 응답하게 된다.
여기서 쿼리에 DISTINCT를 추가하게 되면 우리가 원하는 값이 나올 것이다.
select distinct t from Team t join fetch t.members
Hibernate 6.0 ~ distinct 적용
스프링 부트 3.0 부터 최소 기준이 Hibernate 6.1버전이 되었다.
Hibernate 6.1 환경에서 findAllTeam를 실행하면 다음과 같이 나온다.
DISTINCT를 적지 않아도 하이버네이트가 6.0 버전 부터는 쿼리에 추가하여 보낸다는 것이다. 또한 중복되는 엔티티가 있으면 하이버네이트가 필터링 해준다는 것이다.
주의
주의할 점은 일단 SQL에서 중복된 값을 가져온 후 필터링하는 것이기 때문에 setFirstResult()나 setMaxResult()같이 페이징을 하면 중복된 row를 기준으로 하기 때문에 위험할 수 있다는 것이다.
댓글