n+1问题

  • http://hi.baidu.com/victorlin23/blog/item/8b7a3382670cd9b16d811963.html

Find()方法下FetchType.LAZY、FetchType.EAGER、@Fetch(FetchMode.JOIN)的区别2010-08-27 16:13结论:1.使用
例如:@ManyToOne(fetch = FetchType.LAZY)将会产生N+1的问题。
      2.使用:@ManyToOne(fetch = FetchType.EAGER)将会使用join查询,推断hibernate对eaer进行了优化并不会产生N+1的问题。 
      3.使用:@Fetch(FetchMode.JOIN)将和第2项描述的执行效果和步骤是一样的,都是使用join 可以得出官方文档:Hibernate_Annotations.pdf第62页描述的Table 2.3. Lazy and fetch options equivalent是正确的。完全等价。hibernate重载了EJG的Fetch注解                                      
一、@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "SERVICE_ID", referencedColumnName = "ID")
public SystemServiceInfo getSysService() {
   return sysService;
}
使用Lazy查询,由于需要立即访问触发了数据库查询。这个时候出现了N+1的问题:
    select
        count(*) as y0_ 
    from
        at_t_client_and_service this_ 
    inner join
        AT_T_CLIENT_INFO clientinfo1_ 
            on this_.CLIENT_ID=clientinfo1_.id 
    where
        (
            1=1
        ) 
        and (
            clientinfo1_.id = ?
        )
Hibernate: 
    select
        * 
    from
        ( select
            this_.id as id56_1_,
            this_.URL as URL56_1_,
            this_.CLIENT_ID as CLIENT15_56_1_,
            this_.CREATE_DATE as CREATE3_56_1_,
            this_.SERVICE_ID as SERVICE16_56_1_,
            this_.NAME_SPACE_URL as NAME4_56_1_,
            this_.LOCAL_PART_SERVICE as LOCAL5_56_1_,
            this_.LOCAL_PART_PORT as LOCAL6_56_1_,
            this_.PERIOD_BEGIN_DATE as PERIOD7_56_1_,
            this_.PERIOD_END_DATE as PERIOD8_56_1_,
            this_.FTP_FLAG as FTP9_56_1_,
            this_.FTP_RELATIVE_FLAG as FTP10_56_1_,
            this_.FTP_PATH as FTP11_56_1_,
            this_.FTP_PORT as FTP12_56_1_,
            this_.FTP_USER as FTP13_56_1_,
            this_.FTP_PASSWORD as FTP14_56_1_,
            clientinfo1_.id as id63_0_,
            clientinfo1_.CLIENT_ID as CLIENT2_63_0_,
            clientinfo1_.CLIENT_IP as CLIENT3_63_0_,
            clientinfo1_.CLIENT_NAME as CLIENT4_63_0_,
            clientinfo1_.REGEST_TIME as REGEST5_63_0_,
            clientinfo1_.REMARK as REMARK63_0_ 
        from
            at_t_client_and_service this_ 
        inner join
            AT_T_CLIENT_INFO clientinfo1_ 
                on this_.CLIENT_ID=clientinfo1_.id 
        where
            (
                1=1
            ) 
            and (
                clientinfo1_.id = ?
            ) ) 
    where
        rownum <= ?
Hibernate: 
    select
        systemserv0_.id as id28_0_,
        systemserv0_.COMM as COMM28_0_,
        systemserv0_.SERVICE_NAME as SERVICE3_28_0_,
        systemserv0_.CREATE_DATE as CREATE4_28_0_ 
    from
        AT_T_SERVICE systemserv0_ 
    where
        systemserv0_.id=?
Hibernate: 
    select
        systemserv0_.id as id28_0_,
        systemserv0_.COMM as COMM28_0_,
        systemserv0_.SERVICE_NAME as SERVICE3_28_0_,
        systemserv0_.CREATE_DATE as CREATE4_28_0_ 
    from
        AT_T_SERVICE systemserv0_ 
    where
        systemserv0_.id=?


二、@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "SERVICE_ID", referencedColumnName = "ID")
public SystemServiceInfo getSysService() {
   return sysService;
}
并未出现N+1的问题
Hibernate: 
    select
        count(*) as y0_ 
    from
        at_t_client_and_service this_ 
    inner join
        AT_T_CLIENT_INFO clientinfo1_ 
            on this_.CLIENT_ID=clientinfo1_.id 
    where
        (
            1=1
        ) 
        and (
            clientinfo1_.id = ?
        )
Hibernate: 
    select
        * 
    from
        ( select
            this_.id as id61_2_,
            this_.URL as URL61_2_,
            this_.CLIENT_ID as CLIENT15_61_2_,
            this_.CREATE_DATE as CREATE3_61_2_,
            this_.SERVICE_ID as SERVICE16_61_2_,
            this_.NAME_SPACE_URL as NAME4_61_2_,
            this_.LOCAL_PART_SERVICE as LOCAL5_61_2_,
            this_.LOCAL_PART_PORT as LOCAL6_61_2_,
            this_.PERIOD_BEGIN_DATE as PERIOD7_61_2_,
            this_.PERIOD_END_DATE as PERIOD8_61_2_,
            this_.FTP_FLAG as FTP9_61_2_,
            this_.FTP_RELATIVE_FLAG as FTP10_61_2_,
            this_.FTP_PATH as FTP11_61_2_,
            this_.FTP_PORT as FTP12_61_2_,
            this_.FTP_USER as FTP13_61_2_,
            this_.FTP_PASSWORD as FTP14_61_2_,
            clientinfo1_.id as id63_0_,
            clientinfo1_.CLIENT_ID as CLIENT2_63_0_,
            clientinfo1_.CLIENT_IP as CLIENT3_63_0_,
            clientinfo1_.CLIENT_NAME as CLIENT4_63_0_,
            clientinfo1_.REGEST_TIME as REGEST5_63_0_,
            clientinfo1_.REMARK as REMARK63_0_,
            systemserv4_.id as id29_1_,
            systemserv4_.COMM as COMM29_1_,
            systemserv4_.SERVICE_NAME as SERVICE3_29_1_,
            systemserv4_.CREATE_DATE as CREATE4_29_1_ 
        from
            at_t_client_and_service this_ 
        inner join
            AT_T_CLIENT_INFO clientinfo1_ 
                on this_.CLIENT_ID=clientinfo1_.id 
        left outer join
            AT_T_SERVICE systemserv4_ 
                on this_.SERVICE_ID=systemserv4_.id 
        where
            (
                1=1
            ) 
            and (
                clientinfo1_.id = ?
            ) ) 
    where
        rownum <= ?


三、 @ManyToOne
@Fetch(FetchMode.JOIN)
@JoinColumn(name = "CLIENT_ID", referencedColumnName = "ID")
public ClientInfo getClientInfo() {
   return clientInfo;
}

未出现N+1的问题。而且和第二种配置的效果完全一样,证实了hibernate做eager时进行了优化,使用了ourtjoin,并不会产生N+1问题。
Hibernate: 
    select
        count(*) as y0_ 
    from
        at_t_client_and_service this_ 
    inner join
        AT_T_CLIENT_INFO clientinfo1_ 
            on this_.CLIENT_ID=clientinfo1_.id 
    where
        (
            1=1
        ) 
        and (
            clientinfo1_.id = ?
        )
Hibernate: 
    select
        * 
    from
        ( select
            this_.id as id35_2_,
            this_.URL as URL35_2_,
            this_.CLIENT_ID as CLIENT15_35_2_,
            this_.CREATE_DATE as CREATE3_35_2_,
            this_.SERVICE_ID as SERVICE16_35_2_,
            this_.NAME_SPACE_URL as NAME4_35_2_,
            this_.LOCAL_PART_SERVICE as LOCAL5_35_2_,
            this_.LOCAL_PART_PORT as LOCAL6_35_2_,
            this_.PERIOD_BEGIN_DATE as PERIOD7_35_2_,
            this_.PERIOD_END_DATE as PERIOD8_35_2_,
            this_.FTP_FLAG as FTP9_35_2_,
            this_.FTP_RELATIVE_FLAG as FTP10_35_2_,
            this_.FTP_PATH as FTP11_35_2_,
            this_.FTP_PORT as FTP12_35_2_,
            this_.FTP_USER as FTP13_35_2_,
            this_.FTP_PASSWORD as FTP14_35_2_,
            clientinfo1_.id as id47_0_,
            clientinfo1_.CLIENT_ID as CLIENT2_47_0_,
            clientinfo1_.CLIENT_IP as CLIENT3_47_0_,
            clientinfo1_.CLIENT_NAME as CLIENT4_47_0_,
            clientinfo1_.REGEST_TIME as REGEST5_47_0_,
            clientinfo1_.REMARK as REMARK47_0_,
            systemserv4_.id as id58_1_,
            systemserv4_.COMM as COMM58_1_,
            systemserv4_.SERVICE_NAME as SERVICE3_58_1_,
            systemserv4_.CREATE_DATE as CREATE4_58_1_ 
        from
            at_t_client_and_service this_ 
        inner join
            AT_T_CLIENT_INFO clientinfo1_ 
                on this_.CLIENT_ID=clientinfo1_.id 
        left outer join
            AT_T_SERVICE systemserv4_ 
                on this_.SERVICE_ID=systemserv4_.id 
        where
            (
                1=1
            ) 
            and (
                clientinfo1_.id = ?
            ) ) 
    where
        rownum <= ?

参考官方文档:Hibernate_Annotations.pdf   P62。The Hibernate annotations overrides the EJB3 fetching options



 



你可能感兴趣的:(问题)