Hibernate里的Fetch的作用

本文探讨了Hibernate中Fetch特性的重要性,特别是在提高Parent与Child关联查询效率方面。通过代码示例介绍了如何利用Fetch解决懒加载(Lazy)带来的性能问题,避免N+1查询现象,并确保数据一致性。

大家都知道,在Hibernate里为了性能考虑,引进了lazy的概念,这里我们以Parent和Child为模型来说明

  1. public class Parent implements Serializable {  
  2.  
  3.     /** identifier field */  
  4.     private Long id;  
  5.  
  6.     /** persistent field */  
  7.     private List childs;  
  8.  
  9.     //skip all getter/setter method  
  10.  
  11.      
  12. }     
  13.  
  14.  
  15.  
  16. public class Child implements Serializable {  
  17.  
  18.     /** identifier field */  
  19.     private Long id;  
  20.  
  21.     /** persistent field */  
  22.     private net.foxlog.model.Parent parent;  
  23.  
  24.     //skip all getter/setter method  
  25.  

在我们查询Parent对象的时候,默认只有Parent的内容,并不包含childs的信息,如果在Parent.hbm.xml里设置lazy="false"的话才同时取出关联的所有childs内容.

问题是我既想要Hibernate默认的性能又想要临时的灵活性该怎么办?  这就是Fetch的功能。我们可以把fetch与lazy="true"的关系类比为事务当中的编程式事务与声明式事务,不太准确,但是大概是这个意思。

总值,fetch就是在代码这一层给你一个主动抓取得机会.

  1. Parent parent = (Parent)hibernateTemplate.execute(new HibernateCallback() {  
  2.             public Object doInHibernate(Session session) throws HibernateException, SQLException {  
  3.                 Query q = session.createQuery(  
  4.                         "from Parent as parent "+  
  5.                                 " left outer join fetch parent.childs " +  
  6.                                 " where parent.id = :id"  
  7.                 );  
  8.                 q.setParameter("id",new Long(15));  
  9.                 return (Parent)q.uniqueResult();  
  10.             }  
  11.  
  12.         });  
  13.  
  14.         Assert.assertTrue(parent.getChilds().size() > 0); 

你可以在lazy="true"的情况下把Fetch去掉,就会报异常. 当然,如果lazy="false"就不需要fetch了有一个问题,使用Fetch会有重复记录的现象发生,我们可以理解为Fetch实际上不是为Parent服务的,而是为Child服务的.所以直接取Parent会有不匹配的问题.

参考一下下面的这篇文章 Hibernate集合初始化

update:以上有些结论错误,实际上在Hibernate3.2.1版本下测试,可以不出现重复记录,

  1. public void testNPlusOne() throws Exception{  
  2.         List list = (List)hibernateTemplate.execute(new HibernateCallback() {  
  3.             public Object doInHibernate(Session session) throws HibernateException, SQLException {  
  4.                 Query q = session.createQuery(  
  5.                         "select distinct p from net.foxlog.model.Parent p inner join fetch p.childs"  
  6.                 );  
  7.                 return q.list();  
  8.             }  
  9.  
  10.         });  
  11.  
  12.         //((Parent)(list.get(0))).getChilds();  
  13.         System.out.println("list size = " + list.size());  
  14.         for(int i=0;i<list.size();i++){  
  15.             Parent p = (Parent)list.get(i);  
  16.             System.out.println("===parent = " + p);  
  17.             System.out.println("===parent's child's length = " + p.getChilds().size());  
  18.         }  
  19.  
  20.     } 

打印结果如下:

  1. Hibernate: select distinct parent0_.id as id2_0_, childs1_.id as id0_1_, childs1_.parent_id as parent2_0_1_, childs1_.parent_id as parent2_0__, childs1_.id as id0__ from parent parent0_ inner join child childs1_ on parent0_.id=childs1_.parent_id  
  2. list size = 3 
  3. ===parent = net.foxlog.model.Parent@1401d28[id=14]  
  4. ===parent's child's length = 1 
  5. ===parent = net.foxlog.model.Parent@14e0e90[id=15]  
  6. ===parent's child's length = 2 
  7. ===parent = net.foxlog.model.Parent@62610b[id=17]  
  8. ===parent's child's length = 3 

另外,如果用open session in view模式的话一般不用Fetch,但首先推荐Fetch,如果非用的话因为有N+1的现象,所以可以结合batch模式来改善下性能.

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值