Skip to content
정명주(myeongju.jung) edited this page Dec 22, 2017 · 7 revisions

Prolog

JPA 연관관계 기본 로딩 전략

  • OneToOne(child) : EAGER
  • OneToOne(parent) : LAZY
  • OneToMany : LAZY
  • ManyToOne : EAGER

// FIXME 위 내용이 맞는지 확인요망

하지만..

모든 로딩 전략은 LAZY로 설정하고, 필요에 따라서 EAGER 설정을 하는 것이 나은 것 같다.

Eager by QueryDsl

  • fetchJoin()을 이용하면 됨
  • querydsl version : 4.1.4 기준

NoticeRepositoryImpl.java

public NoticeRepositoryImpl extends BaseRepositorySupport implements NoticeCustomRepository {
    // ...
    public Page<NoticeListDetail> findListDetails(Predicate predicate, Pageable pageable) {
        QNotice notice = QNotice.notice;
        QPartner partner = QPartner.partner;

        JPQLQuery<NoticeListDetail> innerQuery = from(notice)
                .innerJoin(notice.partner, partner)
                .fetchJoin()    // EAGER 로딩
                .where(predicate)
                .select(new QNoticeListDetail(notice, partner));
        return queryToPage(innerQuery, pageable);
    }

만약 모든 join에 대해서 즉시로딩 전략을 취하고 싶으면 fetchAll() 연산자를 사용

BaseRepositorySupport.java

/**
 * 공통 저장소 서포트
 * 상속을 통해서 공통코드를 관리
 *
 * @author myeongju.jung
 */
public abstract class BaseRepositorySupport extends QueryDslRepositorySupport {
    public BaseRepositorySupport(Class<?> domainClass) {
        super(domainClass);
    }
    /**
     * 일반 JPQLQuery를 Page로 변환
     *
     * @param query    내부 JPQLQuery
     * @param pageable 페이징 인자
     * @param <T>      반환 타입
     * @return 페이징 결과
     */
    protected final <T> Page<T> queryToPage(JPQLQuery<T> query, Pageable pageable) {
        long count = query.fetchCount();

        List<T> content = Collections.emptyList();
        if (count > 0) {
            content = getQuerydsl().applyPagination(pageable, query).fetch();
        }
        return new PageImpl<>(content, pageable, count);
    }
}

Eager by spring-data-jpa

AdRepository.java

public interface AdRepository extends JpaRepository<Ad, Long>, QueryDslPredicateExecutor<Ad> {
    @EntityGraph("Ad.withCompany")
    Page<Ad> findAll(Predicate predicate, Pageable pageable);
}

Ad.java

@Entity
@NamedEntityGraph(name = "Ad.withCompany", attributeNodes = @NamedAttributeNode("company"))
// ...
public class Ad {
    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "AD_SEQ")
    @Column(name = "AD_NO")
    private Long adNo;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "COMPANY_NO", nullable = false, updatable = false,
            foreignKey = @ForeignKey(name = "FK_AD_COMPANY_NO"))
    private Company company;
    // ...
}
  • @EntityGraph("Ad.withCompany") 때문에 바로 ad와 company가 join(EAGER) 되어 조회된다.
  • @EntityGraph에서 연관관계 로딩 전략을 직접 설정하기 보다는 해당 Entity에서 @NamedEntityGraph로 선언한 것을 가져다 쓰는 것을 추천
    • 대부분의 경우 재사용하게 되는 경우가 많으며, 연관관계에 대한 로딩 전략을 Entity에서 관리할 수 있다.

연관관계의 연관관계까지 즉시 로딩

Partner, Company and Ad

  • 만약 위와 같은 도메인 모델 상황에서 Ad 목록을 조회할 시 Company 뿐만 아니라 Partner까지 즉시(EAGER) 로딩할려면?

AdRepository.java

public interface AdRepository extends JpaRepository<Ad, Long>, QueryDslPredicateExecutor<Ad> {
    @EntityGraph("Ad.withCompanyAndPartner")
    Page<Ad> findAll(Predicate predicate, Pageable pageable);
}

Ad.java

@Entity
@NamedEntityGraph(name = "Ad.withCompanyAndPartner",
        attributeNodes = @NamedAttributeNode(value = "company", subgraph = "companyWithPartner"),
        subgraphs = @NamedSubgraph(name = "companyWithPartner", attributeNodes = @NamedAttributeNode("partner")))
// ...
public class Ad {
    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "AD_SEQ")
    @Column(name = "AD_NO")
    private Long adNo;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "COMPANY_NO", nullable = false, updatable = false,
            foreignKey = @ForeignKey(name = "FK_AD_COMPANY_NO"))
    private Company company;
    // ...
}
  • @NamedEntityGraph 설정이 핵심. 해당 부분에서 Ad -> Company -> Partner 까지 객체그래프 설정

Eager By hibernate

// TODO

PlantUml

Partner, Company and Ad

@startuml
class Partner {
}
class Company {
  -partner:Partner
  -ads:List<Ad>
}
class Ad {
  -company:Company
}
Partner <-- "*" Company
Company <--> "*" Ad
@enduml
Clone this wiki locally