티스토리 뷰

JPA 를 사용할때 N+1 문제가 발생합니다. 이를 해결하기 위해 여러방법들을 사용하는데요~!


  • QueryDSL
  • @Query ( org.springframework.data.jpa.repository )
  • @EntityGraph ( org.springframework.data.jpa.repository )

... 아는건 여기까지 ..

위 내용에 대해서는 여러 많은 블로그에 존재합니다~!


위 방법들을 사용해서 N+1 문제를 발생시키지 않고 필요한 도메인 정보를 가져 왔을때

제대로 가져 왔는지 어떻게 테스트를 하지? 하고 고민을하고 구글링으로 찾은 내용 입니다.


사용한방법으로는 Hibernate.isInitialized ( package org.hibernate ) 입니다.

package org.hibernate;
//생략
public final class Hibernate {

//생략

@SuppressWarnings("SimplifiableIfStatement")
public static boolean isInitialized(Object proxy) {
if ( proxy instanceof HibernateProxy ) {
return !( (HibernateProxy) proxy ).getHibernateLazyInitializer().isUninitialized();
}
else if ( proxy instanceof PersistentCollection ) {
return ( (PersistentCollection) proxy ).wasInitialized();
}
else {
return true;
}
}

//생략
}


예제 코드는 QueryDSL 을 사용하여 진행하였습니다.

코드는 GitHUB 에 존재합니다.


간단한 유저 entity 와 게시글 entity 입니다.


@Override
public Optional<Article> findWithArticleByIdx(final Long idx) {
return Optional.ofNullable(
this.queryFactory.selectFrom(article)
.innerJoin(article.user, user)
.fetchJoin()
.where(article.idx.eq(idx))
.fetchOne()
);
}

위 entity 들로 query dsl fetchjoin() 을 사용하여 해당 article 에 대한 user 을 가져 왔습니다. 실행된 쿼리는 아래와 같습니다.


Hibernate: 

    select

        article0_.idx as idx1_0_0_,

        user1_.idx as idx1_3_1_,

        article0_.category as category2_0_0_,

        article0_.content as content3_0_0_,

        article0_.user_idx as user_idx4_0_0_,

        user1_.email as email2_3_1_,

        user1_.name as name3_3_1_,

        user1_.password as password4_3_1_,

        user1_.user_id as user_id5_3_1_ 

    from

        article article0_ 

    inner join

        user user1_ 

            on article0_.user_idx=user1_.idx 

    where

        article0_.idx=?


위 쿼리로 Article entity 를 가져게 되었고 이제 Article entity 를 test 할때에 User entity 를 fetch 하였는지 확인하는 방법입니다.

@Test
public void article_user_fetch_join() {
Article article = this.articleRepository.findWithArticleByIdx(1L)
.orElseThrow(NullPointerException::new);

assertTrue(Hibernate.isInitialized(article.getUser())); //user fetch 확인
}

결과는 성공..! 

Hibernate.isInitialized 로 Article 의 User 객체가 initialized 되었는지 확인을 할수 있었습니다.


반대로 Article 의 User 객체를 lazy load 로 가져올 경우

@Test
public void article_user_not_fetch_join() {
Article article = this.articleRepository.findByIdx(1L)
.orElseThrow(NullPointerException::new);

assertFalse(Hibernate.isInitialized(article.getUser())); //lazy loading 확인
}

Hibernate.isInitialized 가 false 로 찍히게 되며 N+1 쿼리가 발생하게 됩니다.


마무리

JPA 를 사용할때 N+1 문제가 발생하여 이를 해결하는 여러가지 방법으로 N+1 문제를 해결을 합니다.

하지만 junit test 를 진행할때에는 제대로 fetch 가 되었는지 확인을 하지 못하였는데..

위 방법으로 이제 junit test 를 할때에 fetch 가 제대로 되었는지.. N+1 문제는 발생하지 않는지에 대한 테스트가 되게 되었습니다~!

댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/11   »
1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
글 보관함