티스토리 뷰
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
- 개발
- query dsl
- @Valid
- jpa
- DEMONIZE
- insert
- SpringBoot
- gradle
- Spring Boot
- hibernate
- n+1
- spring
- JUnit
- orm
- spring-data-jpa
- web
- query
- SonarQube
- fetchjoin
- Database
- Validation
- fetch join
- ec2
- error
- 페치조인
- Docker
- QueryDSL
- Jenkins
- 쿼리
- Limit
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |