JPA ) N + 1 Problem

CokeBear·2022년 6월 13일
0

spring

목록 보기
2/15

JPA 엔티티 조회 중 N + 1 문제는 정말 많이 일어난다.

아래는 Study Entity를 조회했을때 발생되는 쿼리의 양이다.

2022-06-13 15:53:30.059 DEBUG 29154 --- [nio-8080-exec-5] org.hibernate.SQL                        : 
    select
        study0_.id as id1_4_,
        study0_.closed as closed2_4_,
        study0_.closed_date_time as closed_d3_4_,
        study0_.full_description as full_des4_4_,
        study0_.image as image5_4_,
        study0_.path as path6_4_,
        study0_.published as publishe7_4_,
        study0_.published_date_time as publishe8_4_,
        study0_.recruiting as recruiti9_4_,
        study0_.recruiting_updated_date_time as recruit10_4_,
        study0_.short_description as short_d11_4_,
        study0_.title as title12_4_,
        study0_.use_banner as use_ban13_4_ 
    from
        study study0_ 
    where
        study0_.path=?
2022-06-13 15:53:30.060 TRACE 29154 --- [nio-8080-exec-5] o.h.type.descriptor.sql.BasicBinder      : binding parameter [1] as [VARCHAR] - [my-study]
2022-06-13 15:53:30.083 DEBUG 29154 --- [nio-8080-exec-5] org.hibernate.SQL                        : 
    select
        members0_.study_id as study_id1_6_0_,
        members0_.members_account_id as members_2_6_0_,
        account1_.account_id as account_1_0_1_,
        account1_.joined_at as joined_a2_0_1_,
        account1_.modified_date as modified3_0_1_,
        account1_.email as email4_0_1_,
        account1_.email_token as email_to5_0_1_,
        account1_.email_token_generated_at as email_to6_0_1_,
        account1_.is_valid as is_valid7_0_1_,
        account1_.nickname as nickname8_0_1_,
        account1_.study_created_by_email as study_cr9_0_1_,
        account1_.study_created_by_web as study_c10_0_1_,
        account1_.study_registration_result_by_email_by_email as study_r11_0_1_,
        account1_.study_registration_result_by_email_by_web as study_r12_0_1_,
        account1_.study_updated_by_email as study_u13_0_1_,
        account1_.study_updated_by_web as study_u14_0_1_,
        account1_.password as passwor15_0_1_,
        account1_.phone as phone16_0_1_,
        account1_.bio as bio17_0_1_,
        account1_.company as company18_0_1_,
        account1_.image as image19_0_1_,
        account1_.job as job20_0_1_,
        account1_.location as locatio21_0_1_,
        account1_.url as url22_0_1_ 
    from
        study_members members0_ 
    inner join
        account account1_ 
            on members0_.members_account_id=account1_.account_id 
    where
        members0_.study_id=?
2022-06-13 15:53:30.083 TRACE 29154 --- [nio-8080-exec-5] o.h.type.descriptor.sql.BasicBinder      : binding parameter [1] as [BIGINT] - [89]
2022-06-13 15:53:30.085 DEBUG 29154 --- [nio-8080-exec-5] org.hibernate.SQL                        : 
    select
        tags0_.study_id as study_id1_7_0_,
        tags0_.tags_id as tags_id2_7_0_,
        tag1_.id as id1_9_1_,
        tag1_.title as title2_9_1_ 
    from
        study_tags tags0_ 
    inner join
        tag tag1_ 
            on tags0_.tags_id=tag1_.id 
    where
        tags0_.study_id=?
2022-06-13 15:53:30.085 TRACE 29154 --- [nio-8080-exec-5] o.h.type.descriptor.sql.BasicBinder      : binding parameter [1] as [BIGINT] - [89]
2022-06-13 15:53:30.085 DEBUG 29154 --- [nio-8080-exec-5] org.hibernate.SQL                        : 
    select
        zones0_.study_id as study_id1_8_0_,
        zones0_.zones_id as zones_id2_8_0_,
        zone1_.id as id1_10_1_,
        zone1_.city as city2_10_1_,
        zone1_.local_name_of_city as local_na3_10_1_,
        zone1_.province as province4_10_1_ 
    from
        study_zones zones0_ 
    inner join
        zone zone1_ 
            on zones0_.zones_id=zone1_.id 
    where
        zones0_.study_id=?
2022-06-13 15:53:30.086 TRACE 29154 --- [nio-8080-exec-5] o.h.type.descriptor.sql.BasicBinder      : binding parameter [1] as [BIGINT] - [89]
2022-06-13 15:53:30.088 DEBUG 29154 --- [nio-8080-exec-5] org.hibernate.SQL                        : 
    select
        managers0_.study_id as study_id1_5_0_,
        managers0_.managers_account_id as managers2_5_0_,
        account1_.account_id as account_1_0_1_,
        account1_.joined_at as joined_a2_0_1_,
        account1_.modified_date as modified3_0_1_,
        account1_.email as email4_0_1_,
        account1_.email_token as email_to5_0_1_,
        account1_.email_token_generated_at as email_to6_0_1_,
        account1_.is_valid as is_valid7_0_1_,
        account1_.nickname as nickname8_0_1_,
        account1_.study_created_by_email as study_cr9_0_1_,
        account1_.study_created_by_web as study_c10_0_1_,
        account1_.study_registration_result_by_email_by_email as study_r11_0_1_,
        account1_.study_registration_result_by_email_by_web as study_r12_0_1_,
        account1_.study_updated_by_email as study_u13_0_1_,
        account1_.study_updated_by_web as study_u14_0_1_,
        account1_.password as passwor15_0_1_,
        account1_.phone as phone16_0_1_,
        account1_.bio as bio17_0_1_,
        account1_.company as company18_0_1_,
        account1_.image as image19_0_1_,
        account1_.job as job20_0_1_,
        account1_.location as locatio21_0_1_,
        account1_.url as url22_0_1_ 
    from
        study_managers managers0_ 
    inner join
        account account1_ 
            on managers0_.managers_account_id=account1_.account_id 
    where
        managers0_.study_id=?
2022-06-13 15:53:30.088 TRACE 29154 --- [nio-8080-exec-5] o.h.type.descriptor.sql.BasicBinder      : binding parameter [1] as [BIGINT] - [89]

나는 Study entity 하나만 조회했을 뿐인데

5번의 쿼리가 나갔다.

JPA 가 연관된 테이블들을 한번에 조회하지 않고

지연로딩으로 되어 있는 필드들을 조회할때마다 join하여 조회하기 때문에 발생하는 현상이다.

이러한 현상을 해결하기 위한 방법은 여러가지가 있지만

이번에 알아볼 방법은 @EntityGraph 어노테이션을 활용한 방법에 대해 포스팅 하겠다.

@NamedEntityGraph

name : 엔티티 그래프 이름 정의 
attributeNodes : 함께 조회할 속성 선택
  • @Entity에서 재사용할 여러 엔티티 그룹을 정의할 때 사용
  • @NamedEntityGraph를 여러개 정의 할 수도 있음
@NamedEntityGraph(name = "Study.withAll",
attributeNodes = {
        @NamedAttributeNode("tags"),
        @NamedAttributeNode("zones"),
        @NamedAttributeNode("managers"),
        @NamedAttributeNode("members"),
})

Study.withAll이라는 이름을 가지고 tags, zones, managers, members 네 가지 attribute에 대해 Lazy 로딩을 사용하지 않겠다는 뜻.

Repository 적용

@EntityGraph(value = "Study.withAll", type = EntityGraph.EntityGraphType.LOAD)
    Study findByPath(String path);

@EntityGraph

value : 사용할 EntityGraph의 이름
type : EntityGraphType.Fetch && Load

결과

2022-06-13 16:16:25.728 DEBUG 30751 --- [nio-8080-exec-5] org.hibernate.SQL                        : 
    select
        study0_.id as id1_4_0_,
        account2_.account_id as account_1_0_1_,
        tag4_.id as id1_9_2_,
        zone6_.id as id1_10_3_,
        account8_.account_id as account_1_0_4_,
        study0_.closed as closed2_4_0_,
        study0_.closed_date_time as closed_d3_4_0_,
        study0_.full_description as full_des4_4_0_,
        study0_.image as image5_4_0_,
        study0_.path as path6_4_0_,
        study0_.published as publishe7_4_0_,
        study0_.published_date_time as publishe8_4_0_,
        study0_.recruiting as recruiti9_4_0_,
        study0_.recruiting_updated_date_time as recruit10_4_0_,
        study0_.short_description as short_d11_4_0_,
        study0_.title as title12_4_0_,
        study0_.use_banner as use_ban13_4_0_,
        account2_.joined_at as joined_a2_0_1_,
        account2_.modified_date as modified3_0_1_,
        account2_.email as email4_0_1_,
        account2_.email_token as email_to5_0_1_,
        account2_.email_token_generated_at as email_to6_0_1_,
        account2_.is_valid as is_valid7_0_1_,
        account2_.nickname as nickname8_0_1_,
        account2_.study_created_by_email as study_cr9_0_1_,
        account2_.study_created_by_web as study_c10_0_1_,
        account2_.study_registration_result_by_email_by_email as study_r11_0_1_,
        account2_.study_registration_result_by_email_by_web as study_r12_0_1_,
        account2_.study_updated_by_email as study_u13_0_1_,
        account2_.study_updated_by_web as study_u14_0_1_,
        account2_.password as passwor15_0_1_,
        account2_.phone as phone16_0_1_,
        account2_.bio as bio17_0_1_,
        account2_.company as company18_0_1_,
        account2_.image as image19_0_1_,
        account2_.job as job20_0_1_,
        account2_.location as locatio21_0_1_,
        account2_.url as url22_0_1_,
        members1_.study_id as study_id1_6_0__,
        members1_.members_account_id as members_2_6_0__,
        tag4_.title as title2_9_2_,
        tags3_.study_id as study_id1_7_1__,
        tags3_.tags_id as tags_id2_7_1__,
        zone6_.city as city2_10_3_,
        zone6_.local_name_of_city as local_na3_10_3_,
        zone6_.province as province4_10_3_,
        zones5_.study_id as study_id1_8_2__,
        zones5_.zones_id as zones_id2_8_2__,
        account8_.joined_at as joined_a2_0_4_,
        account8_.modified_date as modified3_0_4_,
        account8_.email as email4_0_4_,
        account8_.email_token as email_to5_0_4_,
        account8_.email_token_generated_at as email_to6_0_4_,
        account8_.is_valid as is_valid7_0_4_,
        account8_.nickname as nickname8_0_4_,
        account8_.study_created_by_email as study_cr9_0_4_,
        account8_.study_created_by_web as study_c10_0_4_,
        account8_.study_registration_result_by_email_by_email as study_r11_0_4_,
        account8_.study_registration_result_by_email_by_web as study_r12_0_4_,
        account8_.study_updated_by_email as study_u13_0_4_,
        account8_.study_updated_by_web as study_u14_0_4_,
        account8_.password as passwor15_0_4_,
        account8_.phone as phone16_0_4_,
        account8_.bio as bio17_0_4_,
        account8_.company as company18_0_4_,
        account8_.image as image19_0_4_,
        account8_.job as job20_0_4_,
        account8_.location as locatio21_0_4_,
        account8_.url as url22_0_4_,
        managers7_.study_id as study_id1_5_3__,
        managers7_.managers_account_id as managers2_5_3__ 
    from
        study study0_ 
    left outer join
        study_members members1_ 
            on study0_.id=members1_.study_id 
    left outer join
        account account2_ 
            on members1_.members_account_id=account2_.account_id 
    left outer join
        study_tags tags3_ 
            on study0_.id=tags3_.study_id 
    left outer join
        tag tag4_ 
            on tags3_.tags_id=tag4_.id 
    left outer join
        study_zones zones5_ 
            on study0_.id=zones5_.study_id 
    left outer join
        zone zone6_ 
            on zones5_.zones_id=zone6_.id 
    left outer join
        study_managers managers7_ 
            on study0_.id=managers7_.study_id 
    left outer join
        account account8_ 
            on managers7_.managers_account_id=account8_.account_id 
    where
        study0_.path=?
2022-06-13 16:16:25.729 TRACE 30751 --- [nio-8080-exec-5] o.h.type.descriptor.sql.BasicBinder      : binding parameter [1] as [VARCHAR] - [my-study]
profile
back end developer

0개의 댓글