[spring-data] @EntityGraph로 lazy 패치 같이 불러오기 (fetch-graph 커스터마이징)

by 스뎅(thDeng) on

spring-data-jpa로 JPA를 쓰다가 보면 LAZY 패치타입으로 relation이 달려 있는 entity를 n+1 문제 없이 한번에 가져오고 싶을 때가 있다. 평소에는 LAZY로 쓰지만, 특정 시나리오에서는 한번에 패치하는게 필요하기도 하다.

이럴 때는 repository 메소드에 @EntityGraph만 달아주면 손쉽게 join 해서 한번에 패치해 올 수 있다.

// getter/setter and annotations ..
@Entity
@Table(name = "user")
public class User {

    // other properties

    @ToString.Exclude
    @OneToMany(fetch = FetchType.LAZY)
    @JoinColumn(name = "user_id")
    private List<Address> addresses = new ArrayList<>();
}

@Repository
public interface UserRepository extends JpaRepository<User, Long> {
    Optional<User> findById(Long userId);

    @EntityGraph(attributePaths = {"addresses"}, type = EntityGraph.EntityGraphType.LOAD)
    Optional<User> findWithAddressesById(Long userId);
}

addressesLAZY이기 때문에 findById로 패치한 User 엔티티에서 addresses에 접근하면 그 때 달려 있는 addresses 개수 만큼 select 쿼리를 날린다. 하지만 findWithAddressesById@EntityGraph 어노테이션으로 addresses도 함께 패치해 오도록 해두었기 때문에 1번의 fetch join 쿼리만 실행된다.

/*
 * findById 쿼리
 */
select
    user0_.id as id1_10_0_,
    user0_.username as username2_10_0_
from
    user user0_
where
    user0_.id=?

/*
 * @EntityGraph 달린 findWithAddressesById 쿼리
 */
select
    user0_.id as id1_10_0_,
    addresses1_.id as id1_5_1_,
    user0_.username as username2_10_0_,
    addresses1_.street as street2_5_1_,
    addresses1_.user_id as user_id3_5_0__,
    addresses1_.id as id1_5_0__
from
    user user0_
left outer join
    address addresses1_
        on user0_.id=addresses1_.user_id
where
    user0_.id=?

가끔 필요한 경우를 위해 fetch type을 바꿀 필요도 없고, Querydsl이나 JPQL 같이 쿼리를 별도로 만들지 않아도 되기 때문에 편하다.

@EntityGraph의 type은 EntityGraph.EntityGraphType.FETCHEntityGraph.EntityGraphType.LOAD 2가지가 있다.

참고

별도로 명시하지 않을 경우, 이 블로그의 포스트는 다음 라이선스에 따라 사용할 수 있습니다: Creative Commons License CC Attribution-NonCommercial-ShareAlike 4.0 International License