yjksw / yjksw.github.io

💡 공부 내용을 기록하는 기술 블로그
https://yjksw.github.io
BSD Zero Clause License
1 stars 1 forks source link

jpa-default-batch-fetch-size-not-working/ #7

Open utterances-bot opened 2 years ago

utterances-bot commented 2 years ago

하이버네이트 default-batch-fetch-size 가 안되는 현상 😢

💡 Intro JPA를 프로젝트에서 사용하면서 연관 엔티티를 호출할 때 생기는 N+1을 해결한 경험이 있다. 이때 해결 방법으로 hibernate의 를 yml에 설정하여 해결했었다. 참고링크 해결부분 프로젝트를 전반적으로 체크하던 와중에 위 설정에 의한 in query가 실행되지 않고 여전히 N+1 문제가

https://yjksw.github.io/jpa-default-batch-fetch-size-not-working/

bperhaps commented 2 years ago

getReference로 가져온 프록시를 대상 객체에 add하는 경우에는 EntityManager 내부의 batchFetchQueue에 해당 entity가 등록되지 않아서 발생하는 문제입니다.

SessionImpl unwrap = entityManager.unwrap(SessionImpl.class);
        Field persistenceContext = SessionImpl.class.getDeclaredField("persistenceContext");
        persistenceContext.setAccessible(true);
        StatefulPersistenceContext s = (StatefulPersistenceContext) persistenceContext.get(unwrap);
        Field entityEntryContext = StatefulPersistenceContext.class.getDeclaredField("entityEntryContext");
        entityEntryContext.setAccessible(true);
        EntityEntryContext e = (EntityEntryContext) entityEntryContext.get(s);
        Field persistenceContext1 = EntityEntryContext.class.getDeclaredField("persistenceContext");
        persistenceContext1.setAccessible(true);
        StatefulPersistenceContext p = (StatefulPersistenceContext) persistenceContext1.get(e);

        //엔티티 목록을 가지고 온다.
        Field entitiesByKey = p.getClass().getDeclaredField("entitiesByKey");
        entitiesByKey.setAccessible(true);
        Map o = (Map) entitiesByKey.get(p);

        //프록시 객체 목록을 가지고 온다.
        Field proxiesByKey = p.getClass().getDeclaredField("proxiesByKey");
        proxiesByKey.setAccessible(true);
        ConcurrentReferenceHashMap<EntityKey, Object> pp = 
        (ConcurrentReferenceHashMap<EntityKey, Object>) proxiesByKey.get(p);

위 코드를 이용해서 한번 확인해 보세요. StatefulPersistentContext 내부에 batchFetchQueue가 들어있습니다. 그리고 엔티티관련된 디버깅을 할때 위와 같이 디버거를 이용하면 디버거로 내부를 살피는 순간 프록시 객체가 실제 entity를 호출하면서 내부 상태가 변합니다. 따라서 entityManager 내부의 프록시 엔티티, 실제 엔티티 목록을 확인하고 싶다면 위와같이 필드를 추출해서 print시키는 방식으로 해야합니다.

young0264 commented 1 year ago

오래 전 글이지만 댓글남겨요. 저도 비슷한 상황에서 Service 단의 Transactioanl 을 readOnlt=true로 바꾸어 동작은 되게 하였습니다.