1 분 소요

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에는 fromUsertoUser든 proxy가 아닌 실제 데이터가 담겨 있으므로 바로 list로 뽑아서 반환해주면 된다.
추가로, fetch join 사용 시에는 join을 쓸 때처럼 on을 사용하면 안된다!
나머지 조건은 모두 WHERE 절에서 처리하기!

카테고리:

업데이트:

댓글남기기