query specified join fetching, but the owner of the fetched association was not present in the select list
entity 간 관계에서 @OneToMany는 FetchType이 기본으로 LAZY여서 상관이 없지만 (@ManyToMany는 쓰지 않으므로 제외)
@OneToOne, @ManyToOne은 FetchType이 기본으로 EAGER로 설정돼있다. 그대로 사용하면 어떤 entity를 참조할 때
EAGER로 설정돼 있는 연관된 entity까지 한 번에 가져오게 되는데 만약 쓰지 않는 경우 비효율적으로 동작한다고 볼 수 있기
때문에 모두 LAZY로 설정해줬고, DB에 쿼리를 날려 한 번에 가져올 필요가 있을 때만 fetch join으로 엮어서 한 번에 가져
오도록 수정했다. 열심히 수정하고 나서 빌드를 하는데 다음 에러가 발생했다.
query specified join fetching, but the owner of the fetched association was not present in the select list
// FollowRepository.java
public List<User> getFollowings(Long userId) {
return em.createQuery("select tUser from Follow f " +
"join fetch f.fromUser fUser " +
"join fetch f.toUser tUser " +
"where fUser.id = :userId", User.class)
.setParameter("userId", userId)
.getResultList();
}
owner가 Follow
인데 Follow.toUser
를 반환하고 있어서 발생한 문제였다. fetch join을 적용하기 전에는 join으로만 사용해서 문제가
없었으나 적용 후에 발생했다. 어떤 유저의 팔로잉 목록을 한 번에 가져오기 위해서 정의해둔 함수였는데, User를 반환하더라도 fetch
join으로 엮여있는 경우엔 반드시 owner인 Follow
를 select 대상으로 가져와야지 다른걸 가져오면 안된다. 그래서 이렇게 수정했다.
public List<User> getFollowings(Long userId) {
List<Follow> result = em.createQuery("select f from Follow f " +
"join fetch f.fromUser " +
"join fetch f.toUser " +
"where f.fromUser.id = :userId", Follow.class)
.setParameter("userId", userId)
.getResultList();
return result.stream()
.map(Follow::getToUser)
.collect(Collectors.toList());
}
Follow
를 가져오되 어차피 fetch join으로 가져온 Follow
에는 fromUser
든 toUser
든 proxy가 아닌 실제 데이터가 담겨 있으므로
바로 list로 뽑아서 반환해주면 된다.
추가로, fetch join 사용 시에는 join을 쓸 때처럼 on
을 사용하면 안된다!
나머지 조건은 모두 WHERE 절에서 처리하기!
댓글남기기