hibernate ibatis n+1

来源:互联网 发布:正大 数据恢复 编辑:程序博客网 时间:2024/06/01 07:13

转载自:http://antlove.iteye.com/blog/1622749

当Hibernate或Ibatis在处理一对多的时候都存在n+1问题。 

创建数据库 
ACCOUNT 表 
ACCOUNT_ID12
ORDERS 表 
ORDER_IDACCOUNT_ID112131425262
创建JavaBean 
Account.java 
Java代码  收藏代码
  1. public class Account {  
  2.     private Integer accountId;  
  3.     private Set<Orders> ordersSet = new HashSet<Orders>();  
  4.         // 省略 getter setter  
  5.         public String toString() {  
  6.         return "accountId:" + accountId;  
  7.     }  
  8. }  

Orders.java 
Java代码  收藏代码
  1. public class Orders {  
  2.     private Integer orderId;  
  3.     private Account account;  
  4.         // 省略getter setter  
  5.     public String toString() {  
  6.         return "orderId:"+orderId;  
  7.     }  
  8. }  

一、使用Ibatis查询 
SqlMapConfig.xml配置 
Xml代码  收藏代码
  1. <sqlMapConfig>  
  2.     <settings cacheModelsEnabled="true" enhancementEnabled="false"  
  3.         lazyLoadingEnabled="false" maxRequests="32" maxSessions="10"  
  4.         maxTransactions="5" useStatementNamespaces="true" />  
  5.     <transactionManager type="JDBC">    
  6.         <dataSource type="SIMPLE">     
  7.             <property name="JDBC.Driver"   
  8.                       value="com.microsoft.sqlserver.jdbc.SQLServerDriver" />   
  9.             <property name="JDBC.ConnectionURL"   
  10.                       value="jdbc:sqlserver://127.0.0.1:1433;DatabaseName=TEST" />   
  11.             <property name="JDBC.Username" value="xiao" />   
  12.             <property name="JDBC.Password" value="xiao" />   
  13.         </dataSource>   
  14.     </transactionManager>     
  15.     <sqlMap resource="ibatis/n1.ibatis.xml" />  
  16.     <sqlMap resource="ibatis/groupby.ibatis.xml" />  
  17. </sqlMapConfig>  

n1.ibatis.xml 配置 
Xml代码  收藏代码
  1. <sqlMap namespace="N1">  
  2.     <typeAlias alias="Account" type="bean.Account"/>  
  3.     <resultMap id="ResultAccountInfoMap" class="bean.Account">  
  4.         <result property="accountId" column="ACCOUNT_ID" />  
  5.         <result property="ordersSet" select="N1.getOrderInfoList" column="ACCOUNT_ID"/>  
  6.     </resultMap>  
  7.     <resultMap id="ResultOrderInfoMap" class="bean.Orders">  
  8.         <result property="orderId" column="ORDER_ID" />  
  9.         <result property="account.accountId" column="ACCOUNt_Id" />  
  10.     </resultMap>  
  11.     <select id="getAccountInfoList" resultMap="ResultAccountInfoMap">  
  12.         select account_Id from ACCOUNT   
  13.     </select>  
  14.     <select id="getOrderInfoList" resultMap="ResultOrderInfoMap">  
  15.         select ORDER_ID,ACCOUNT_ID from ORDERS where ACCOUNT_ID = #value#  
  16.     </select>  
  17. </sqlMap>  

groupby.ibatis.xml 配置 
Xml代码  收藏代码
  1. <sqlMap namespace="GroupBy">  
  2.     <resultMap id="ResultAccountInfoMap" class="bean.Account" groupBy="accountId">  
  3.         <result property="accountId" column="account_Id" />  
  4.         <result property="ordersSet" resultMap="GroupBy.ResultOrderInfoMap"/>  
  5.     </resultMap>  
  6.     <resultMap id="ResultOrderInfoMap" class="bean.Orders" groupBy="orderId">  
  7.         <result property="orderId" column="ORDER_ID" />  
  8.         <result property="account.accountId" column="ACCOUNT_ID" />  
  9.     </resultMap>  
  10.     <select id="getAccountInfoList" resultMap="ResultAccountInfoMap">  
  11.          select   
  12.          ACCOUNT.ACCOUNT_ID AS ACCOUNT_ID  
  13.         ,ORDERS.ORDER_ID AS ORDER_ID  
  14.         from ACCOUNT   
  15.         join ORDERS on ACCOUNT.account_Id=ORDERS.ACCOUNT_ID  
  16.         group by ACCOUNT.ACCOUNT_ID,ORDERS.ORDER_ID  
  17.     </select>  
  18. </sqlMap>  

测试代码 
Java代码  收藏代码
  1. public static void queryN1() throws SQLException, IOException{  
  2.         Reader reader=Resources.getResourceAsReader("ibatis/SqlMapConfig.xml");  
  3.         SqlMapClient sqlMapClient = SqlMapClientBuilder.buildSqlMapClient(reader);  
  4.           
  5.         List<Account> accountList=sqlMapClient.queryForList("N1.getAccountInfoList");  
  6.         for(Account account:accountList){  
  7.             System.out.println("############"+account+"############");  
  8.             for(Orders orders:account.getOrdersSet()){  
  9.                 System.out.println("############"+orders+"############");  
  10.             }  
  11.         }  
  12. }  

执行以上代码,log4j打印如下: 
Java代码  收藏代码
  1. [size=xx-small]2012-08-07 10:19:52,000-[HL] DEBUG main java.sql.Connection - {conn-100000} Connection  
  2.    2012-08-07 10:19:52,000-[HL] DEBUG main java.sql.Connection - {conn-100000} Preparing Statement:    select account_Id from ACCOUNT     
  3.    2012-08-07 10:19:52,031-[HL] DEBUG main java.sql.PreparedStatement - {pstm-100001} Executing Statement:    select account_Id from ACCOUNT     
  4.    2012-08-07 10:19:52,031-[HL] DEBUG main java.sql.PreparedStatement - {pstm-100001} Parameters: []  
  5.    2012-08-07 10:19:52,031-[HL] DEBUG main java.sql.PreparedStatement - {pstm-100001} Types: []  
  6.    2012-08-07 10:19:52,078-[HL] DEBUG main java.sql.Connection - {conn-100000} Preparing Statement:    select ORDER_ID,ACCOUNT_ID from ORDERS where ACCOUNT_ID = ?    
  7.    2012-08-07 10:19:52,078-[HL] DEBUG main java.sql.PreparedStatement - {pstm-100003} Executing Statement:    select ORDER_ID,ACCOUNT_ID from ORDERS where ACCOUNT_ID = ?    
  8.    2012-08-07 10:19:52,078-[HL] DEBUG main java.sql.PreparedStatement - {pstm-100003} Parameters: [1]  
  9.    2012-08-07 10:19:52,078-[HL] DEBUG main java.sql.PreparedStatement - {pstm-100003} Types: [java.lang.Integer]  
  10.    2012-08-07 10:19:52,078-[HL] DEBUG main java.sql.PreparedStatement - {pstm-100003} Executing Statement:    select ORDER_ID,ACCOUNT_ID from ORDERS where ACCOUNT_ID = ?    
  11.    2012-08-07 10:19:52,078-[HL] DEBUG main java.sql.PreparedStatement - {pstm-100003} Parameters: [2]  
  12.    2012-08-07 10:19:52,078-[HL] DEBUG main java.sql.PreparedStatement - {pstm-100003} Types: [java.lang.Integer][/size][size=xx-small][/size]  
  13.    ############accountId:1############  
  14. ############orderId:3############  
  15. ############orderId:2############  
  16. ############orderId:1############  
  17. ############accountId:2############  
  18. ############orderId:5############  
  19. ############orderId:6############  
  20. ############orderId:4############  

查看以上log,发现执行了3条查询语句. 
将SqlMapConfig.xml中的setting 节点中的lazyLoadingEnabled属性设为true 
Xml代码  收藏代码
  1. <settings cacheModelsEnabled="true" enhancementEnabled="false"  
  2.     lazyLoadingEnabled="true" maxRequests="32" maxSessions="10"  
  3.     maxTransactions="5" useStatementNamespaces="true" />  

再次执行之前queryN1测试代码,log4j打印如下 
Java代码  收藏代码
  1. 2012-08-07 10:26:42,468-[HL] DEBUG main java.sql.Connection - {conn-100000} Connection  
  2.    2012-08-07 10:26:42,468-[HL] DEBUG main java.sql.Connection - {conn-100000} Preparing Statement:    select account_Id from ACCOUNT     
  3.    2012-08-07 10:26:42,515-[HL] DEBUG main java.sql.PreparedStatement - {pstm-100001} Executing Statement:    select account_Id from ACCOUNT     
  4.    2012-08-07 10:26:42,515-[HL] DEBUG main java.sql.PreparedStatement - {pstm-100001} Parameters: []  
  5.    2012-08-07 10:26:42,515-[HL] DEBUG main java.sql.PreparedStatement - {pstm-100001} Types: []  
  6.    ############accountId:1############  
  7. 2012-08-07 10:26:42,562-[HL] DEBUG main java.sql.Connection - {conn-100003} Connection  
  8.    2012-08-07 10:26:42,562-[HL] DEBUG main java.sql.Connection - {conn-100003} Preparing Statement:    select ORDER_ID,ACCOUNT_ID from ORDERS where ACCOUNT_ID = ?    
  9.    2012-08-07 10:26:42,562-[HL] DEBUG main java.sql.PreparedStatement - {pstm-100004} Executing Statement:    select ORDER_ID,ACCOUNT_ID from ORDERS where ACCOUNT_ID = ?    
  10.    2012-08-07 10:26:42,562-[HL] DEBUG main java.sql.PreparedStatement - {pstm-100004} Parameters: [1]  
  11.    2012-08-07 10:26:42,562-[HL] DEBUG main java.sql.PreparedStatement - {pstm-100004} Types: [java.lang.Integer]  
  12.    ############orderId:3############  
  13. ############orderId:1############  
  14. ############orderId:2############  
  15. ############accountId:2############  
  16. 2012-08-07 10:26:42,562-[HL] DEBUG main java.sql.Connection - {conn-100006} Connection  
  17.    2012-08-07 10:26:42,562-[HL] DEBUG main java.sql.Connection - {conn-100006} Preparing Statement:    select ORDER_ID,ACCOUNT_ID from ORDERS where ACCOUNT_ID = ?    
  18.    2012-08-07 10:26:42,562-[HL] DEBUG main java.sql.PreparedStatement - {pstm-100007} Executing Statement:    select ORDER_ID,ACCOUNT_ID from ORDERS where ACCOUNT_ID = ?    
  19.    2012-08-07 10:26:42,562-[HL] DEBUG main java.sql.PreparedStatement - {pstm-100007} Parameters: [2]  
  20.    2012-08-07 10:26:42,562-[HL] DEBUG main java.sql.PreparedStatement - {pstm-100007} Types: [java.lang.Integer]  
  21.    ############orderId:5############  
  22. ############orderId:4############  
  23. ############orderId:6############  

查看log,依然有3条查询语句,只是查询的时机不一样了。当设置SqlMapConfig.xml中的setting 节点中的lazyLoadingEnabled属性设为true时(启动ibatis懒加载),ibatis会生成代理对对象,当集合元素真正使用时才执行查询语句(和hibernate类似)。 
目前来看:至少在ibatis中启用或不启用懒加载,都不可以解决n+1问题.而在ibatis中使用groupby是一个解决方案. 
测试代码如下: 
Java代码  收藏代码
  1. public static void queryGroupBy() throws SQLException, IOException{  
  2.        Reader reader=Resources.getResourceAsReader("ibatis/SqlMapConfig.xml");  
  3.     SqlMapClient sqlMapClient = SqlMapClientBuilder.buildSqlMapClient(reader);  
  4.     List<Account> accountList=sqlMapClient.queryForList("GroupBy.getAccountInfoList");  
  5.     for(Account account:accountList){  
  6.         System.out.println("############"+account+"############");  
  7.         for(Orders orders:account.getOrdersSet()){  
  8.             System.out.println("############"+orders+"############");  
  9.         }  
  10.     }  
  11. }  

执行以上测试代码,log打印如下 
Java代码  收藏代码
  1. 2012-08-07 10:36:12,328-[HL] DEBUG main java.sql.Connection - {conn-100000} Connection  
  2.    2012-08-07 10:36:12,328-[HL] DEBUG main java.sql.Connection - {conn-100000} Preparing Statement:     select     ACCOUNT.ACCOUNT_ID AS ACCOUNT_ID   ,ORDERS.ORDER_ID AS ORDER_ID   from ACCOUNT    join ORDERS on ACCOUNT.account_Id=ORDERS.ACCOUNT_ID   group by ACCOUNT.ACCOUNT_ID,ORDERS.ORDER_ID    
  3.    2012-08-07 10:36:12,359-[HL] DEBUG main java.sql.PreparedStatement - {pstm-100001} Executing Statement:     select     ACCOUNT.ACCOUNT_ID AS ACCOUNT_ID   ,ORDERS.ORDER_ID AS ORDER_ID   from ACCOUNT    join ORDERS on ACCOUNT.account_Id=ORDERS.ACCOUNT_ID   group by ACCOUNT.ACCOUNT_ID,ORDERS.ORDER_ID    
  4.    2012-08-07 10:36:12,359-[HL] DEBUG main java.sql.PreparedStatement - {pstm-100001} Parameters: []  
  5.    2012-08-07 10:36:12,359-[HL] DEBUG main java.sql.PreparedStatement - {pstm-100001} Types: []  
  6.    ############accountId:1############  
  7. ############orderId:3############  
  8. ############orderId:2############  
  9. ############orderId:1############  
  10. ############accountId:2############  
  11. ############orderId:4############  
  12. ############orderId:6############  
  13. ############orderId:5############  

查看以上log,发现只有1条查询语句,解决了n+1问题。 
目前看来,n+1问题算是解决了,可是配置文件搞得很复杂。 
ibatis作为sql mapper并不是orm,非把ibatis当hibernate玩,个人认为这样并没有多大意义。 

二 使用hibernate查询 
hibernate.cfg.xml配置 
Xml代码  收藏代码
  1. <hibernate-configuration>  
  2.     <session-factory>  
  3.         <property name="dialect">org.hibernate.dialect.SQLServerDialect</property>  
  4.         <property name="connection.url">jdbc:sqlserver://127.0.0.1:1433;DatabaseName=TEST</property>  
  5.         <property name="connection.username">xiao</property>  
  6.         <property name="connection.password">xiao</property>  
  7.         <property name="connection.driver_class">com.microsoft.sqlserver.jdbc.SQLServerDriver</property>  
  8.         <property name="show_sql">true</property>  
  9.           
  10.         <mapping resource="hibernate/Account.hibernate.xml"/>  
  11.         <mapping resource="hibernate/Orders.hibernate.xml"/>  
  12.     </session-factory>  
  13. </hibernate-configuration>  

Account.hibernate.xml 配置 
Xml代码  收藏代码
  1. <hibernate-mapping>  
  2.     <class name="bean.Account" table="ACCOUNT" >  
  3.         <id name="accountId" column="ACCOUNT_ID" />  
  4.         <set name="ordersSet" >  
  5.             <key column="ACCOUNT_ID"/>  
  6.             <one-to-many class="bean.Orders"/>  
  7.         </set>  
  8.     </class>  
  9. </hibernate-mapping>    

Orders.hibernate.xml 配置 
Xml代码  收藏代码
  1. <hibernate-mapping>  
  2.    <class name="bean.Orders" table="ORDERS">  
  3.     <id name="orderId" column="ORDER_ID"/>  
  4.     <many-to-one name="account" class="bean.Account" column="ACCOUNT_ID"/>  
  5.    </class>  
  6. </hibernate-mapping>   

a.hibernate 1对多 n+1 
测试代码 
Java代码  收藏代码
  1. public static void query1(){  
  2.        Configuration config = new Configuration().configure("hibernate/hibernate.cfg.xml");    
  3.        SessionFactory factory = config.buildSessionFactory();    
  4.        Session session = factory.openSession();  
  5.        System.out.println("==========start query==========");  
  6.        Query query=session.createQuery("from Account as account");  
  7.        List<Account> accountList=query.list();  
  8.     for(Account account:accountList){  
  9.         System.out.println("############"+account+"############");  
  10.         for(Orders orders:account.getOrdersSet()){  
  11.             System.out.println("############"+orders+"############");  
  12.         }  
  13.     }  
  14.        session.close();  
  15. }  

执行以上测试代码,打印log如下: 
Java代码  收藏代码
  1.    ==========start query==========  
  2. Hibernate: select account0_.ACCOUNT_ID as ACCOUNT1_0_ from ACCOUNT account0_  
  3. ############accountId:1############  
  4. Hibernate: select ordersset0_.ACCOUNT_ID as ACCOUNT2_1_, ordersset0_.ORDER_ID as ORDER1_1_, ordersset0_.ORDER_ID as ORDER1_1_0_, ordersset0_.ACCOUNT_ID as ACCOUNT2_1_0_ from ORDERS ordersset0_ where ordersset0_.ACCOUNT_ID=?  
  5. ############orderId:2############  
  6. ############orderId:1############  
  7. ############orderId:3############  
  8. ############accountId:2############  
  9. Hibernate: select ordersset0_.ACCOUNT_ID as ACCOUNT2_1_, ordersset0_.ORDER_ID as ORDER1_1_, ordersset0_.ORDER_ID as ORDER1_1_0_, ordersset0_.ACCOUNT_ID as ACCOUNT2_1_0_ from ORDERS ordersset0_ where ordersset0_.ACCOUNT_ID=?  
  10. ############orderId:6############  
  11. ############orderId:5############  
  12. ############orderId:4############  

查看以上log,发现3条查询语句。n+1 问题重现了。因为hibernate对集合查询默认是懒加载的,所以从以上打印log发现,只用在要使用集合时,hibernate才去执行查询。 
将Account.hibernate.xml中set节点的lazy属性设置为false,如下 
Xml代码  收藏代码
  1. <set name="ordersSet" lazy="false">  
  2.     <key column="ACCOUNT_ID"/>  
  3.     <one-to-many class="bean.Orders"/>  
  4. </set>  

再次执行query1()测试代码,log打印如下: 
Java代码  收藏代码
  1.    ==========start query==========  
  2. Hibernate: select account0_.ACCOUNT_ID as ACCOUNT1_0_ from ACCOUNT account0_  
  3. Hibernate: select ordersset0_.ACCOUNT_ID as ACCOUNT2_1_, ordersset0_.ORDER_ID as ORDER1_1_, ordersset0_.ORDER_ID as ORDER1_1_0_, ordersset0_.ACCOUNT_ID as ACCOUNT2_1_0_ from ORDERS ordersset0_ where ordersset0_.ACCOUNT_ID=?  
  4. Hibernate: select ordersset0_.ACCOUNT_ID as ACCOUNT2_1_, ordersset0_.ORDER_ID as ORDER1_1_, ordersset0_.ORDER_ID as ORDER1_1_0_, ordersset0_.ACCOUNT_ID as ACCOUNT2_1_0_ from ORDERS ordersset0_ where ordersset0_.ACCOUNT_ID=?  
  5. ############accountId:1############  
  6. ############orderId:1############  
  7. ############orderId:3############  
  8. ############orderId:2############  
  9. ############accountId:2############  
  10. ############orderId:5############  
  11. ############orderId:4############  
  12. ############orderId:6############  

查看以上log,发现依然是3条查询语句,只是查询在一开始就全部执行了。 
b.hibernate 多对1 n+1 
测试代码 
Java代码  收藏代码
  1. public static void query2(){  
  2.     Configuration config = new Configuration().configure("hibernate/hibernate.cfg.xml");    
  3.     SessionFactory factory = config.buildSessionFactory();    
  4.     Session session = factory.openSession();  
  5.     System.out.println("==========start query==========");  
  6.     Query query=session.createQuery("from Orders");  
  7.     List<Orders> ordersSet=query.list();  
  8.     for(Orders orders:ordersSet){  
  9.         System.out.println("############"+orders+"############");  
  10.         System.out.println("############"+orders.getAccount()+"############");  
  11.     }  
  12.     session.close();  
  13. }  

执行以上测试代码,log如下: 
Java代码  收藏代码
  1.    ==========start query==========  
  2. Hibernate: select orders0_.ORDER_ID as ORDER1_1_, orders0_.ACCOUNT_ID as ACCOUNT2_1_ from ORDERS orders0_  
  3. ############orderId:1############  
  4. Hibernate: select account0_.ACCOUNT_ID as ACCOUNT1_0_0_ from ACCOUNT account0_ where account0_.ACCOUNT_ID=?  
  5. ############accountId:1############  
  6. ############orderId:2############  
  7. ############accountId:1############  
  8. ############orderId:3############  
  9. ############accountId:1############  
  10. ############orderId:4############  
  11. Hibernate: select account0_.ACCOUNT_ID as ACCOUNT1_0_0_ from ACCOUNT account0_ where account0_.ACCOUNT_ID=?  
  12. ############accountId:2############  
  13. ############orderId:5############  
  14. ############accountId:2############  
  15. ############orderId:6############  
  16. ############accountId:2############  

查看以上log,发现3条查询语句。 
倘若将Account.hibernate.xml中set节点的lazy属性设置为false,如下 
Xml代码  收藏代码
  1. <set name="ordersSet" lazy="false">  
  2.     <key column="ACCOUNT_ID"/>  
  3.     <one-to-many class="bean.Orders"/>  
  4. </set>  

再次执行query2(),log打印如下: 
Java代码  收藏代码
  1.    ==========start query==========  
  2. Hibernate: select orders0_.ORDER_ID as ORDER1_1_, orders0_.ACCOUNT_ID as ACCOUNT2_1_ from ORDERS orders0_  
  3. ############orderId:1############  
  4. Hibernate: select account0_.ACCOUNT_ID as ACCOUNT1_0_0_ from ACCOUNT account0_ where account0_.ACCOUNT_ID=?  
  5. Hibernate: select ordersset0_.ACCOUNT_ID as ACCOUNT2_1_, ordersset0_.ORDER_ID as ORDER1_1_, ordersset0_.ORDER_ID as ORDER1_1_0_, ordersset0_.ACCOUNT_ID as ACCOUNT2_1_0_ from ORDERS ordersset0_ where ordersset0_.ACCOUNT_ID=?  
  6. ############accountId:1############  
  7. ############orderId:2############  
  8. ############accountId:1############  
  9. ############orderId:3############  
  10. ############accountId:1############  
  11. ############orderId:4############  
  12. Hibernate: select account0_.ACCOUNT_ID as ACCOUNT1_0_0_ from ACCOUNT account0_ where account0_.ACCOUNT_ID=?  
  13. Hibernate: select ordersset0_.ACCOUNT_ID as ACCOUNT2_1_, ordersset0_.ORDER_ID as ORDER1_1_, ordersset0_.ORDER_ID as ORDER1_1_0_, ordersset0_.ACCOUNT_ID as ACCOUNT2_1_0_ from ORDERS ordersset0_ where ordersset0_.ACCOUNT_ID=?  
  14. ############accountId:2############  
  15. ############orderId:5############  
  16. ############accountId:2############  
  17. ############orderId:6############  
  18. ############accountId:2############  

查看以上log,发现5条查询语句。第一查询语句用来查询Orders信息,而Orders对象中的Account实例变量默认是懒加载的,所以当使用account变量时,hibernate才进行查询,由于Account对象中的ordersSet集合不启用懒加载。所以每次查询Account时会附带查询一次Orders. 

c.hibernate 使用iterate n+1 
测试代码 
Java代码  收藏代码
  1. public static void query3(){  
  2.     Configuration config = new Configuration().configure("hibernate/hibernate.cfg.xml");    
  3.     SessionFactory factory = config.buildSessionFactory();    
  4.     Session session = factory.openSession();  
  5.     System.out.println("==========start query==========");  
  6.     Query query=session.createQuery("from Account");  
  7.     Iterator<Account> iterator=query.iterate();  
  8.     while(iterator.hasNext()){  
  9.         Account account=iterator.next();  
  10.         System.out.println("############"+account+"############");  
  11.         System.out.println("############"+account.getOrdersSet()+"############");  
  12.     }  
  13.     session.close();  
  14. }  

执行以上测试代码,log打印语句如下: 
Java代码  收藏代码
  1.    ==========start query==========  
  2. Hibernate: select account0_.ACCOUNT_ID as col_0_0_ from ACCOUNT account0_  
  3. Hibernate: select account0_.ACCOUNT_ID as ACCOUNT1_0_0_ from ACCOUNT account0_ where account0_.ACCOUNT_ID=?  
  4. ############accountId:1############  
  5. Hibernate: select ordersset0_.ACCOUNT_ID as ACCOUNT2_1_, ordersset0_.ORDER_ID as ORDER1_1_, ordersset0_.ORDER_ID as ORDER1_1_0_, ordersset0_.ACCOUNT_ID as ACCOUNT2_1_0_ from ORDERS ordersset0_ where ordersset0_.ACCOUNT_ID=?  
  6. ############[orderId:3, orderId:2, orderId:1]############  
  7. Hibernate: select account0_.ACCOUNT_ID as ACCOUNT1_0_0_ from ACCOUNT account0_ where account0_.ACCOUNT_ID=?  
  8. ############accountId:2############  
  9. Hibernate: select ordersset0_.ACCOUNT_ID as ACCOUNT2_1_, ordersset0_.ORDER_ID as ORDER1_1_, ordersset0_.ORDER_ID as ORDER1_1_0_, ordersset0_.ACCOUNT_ID as ACCOUNT2_1_0_ from ORDERS ordersset0_ where ordersset0_.ACCOUNT_ID=?  
  10. ############[orderId:6, orderId:5, orderId:4]############  

查看以上log,发现有5条查询语句。第一条查询Account对象的accountId列表。当迭代使用Account时,hibernate会通过查询到的accountId在内存中找,如果找到则直接使用,如果为未找到则做一次查询。而把iterate查询归纳为n+1查询感觉不妥当,写在这里是想说明iterate没用好会产生n+1次查询或者更多。 
如果将query3()中的迭代对象改为Orders,就会有很刺激的事,如果再将Account中的ordersSet懒加载去除(lazy=false),就更有意思了。 
在hibernate中要解决n+1可以通过left join fetch。 
将query1() 的hql该为from Account as account left join fetch account.ordersSet 
重新执行则只有一条查询语句。 

总结:不管是hibernate 还是ibatis 如果对象设计中有主从对象(主从对象指:一个对象中包含另一个对象,包含对象为主对象,被包含对象为从对象,从对象可为普通javabean或集合)都有可能碰到n+1问题,其中1指进行一条语句查询到n个主对象,而n指n个主对象要将从对象关联出来要进行n次查询。而解决方案不管怎样就是通过数据库join查询。而hibernate比较复杂点有很多缓存可用,所以具体的时候会有点差异。n+1并不是问题,更是一种策略。 
0 0