浅谈Hibernate的fetch

Hibernate的fetch="join"和fetch="select" 的一点分析 

fetch参数指定了关联对象抓取的方式是select查询还是join查询,select方式时先查询返回要查询的主体对象(列表),再根据关联外键id,每一个对象发一个select查询,获取关联的对象,形成n+1次查询;
而join方式,主体对象和关联对象用一句外键关联的sql同时查询出来,不会形成多次查询。
如果你的关联对象是延迟加载的,它当然不会去查询关联对象。
另外,在hql查询中配置文件中设置的join方式是不起作用的(而在所有其他查询方式如get、criteria或再关联获取等等都是有效的),会使用select方式,除非你在hql中指定join fetch某个关联对象。

 

fetch策略用于定义 get/load一个对象时,如何获取非lazy的对象/集合。 这些参数在Query中无效。

fetch策略用于定义 get/load一个对象时,如何获取非lazy的对象/集合。 这些参数在Query中无效。

 

查询抓取(默认的)在N+1查询的情况下是不好的,因此我们可能会要求在映射文档中定义使用连接抓取:

 


fetch="join">



在映射文档中定义的抓取策略将会有产生以下影响:

通过get()或load()方法取得数据。

只有在关联之间进行导航时,才会隐式的取得数据(延迟抓取)。

条件查询

在映射文档中显式的声明连接抓取做为抓取策略并不会影响到随后的HQL查询。

通常情况下,我们并不使用映射文档进行抓取策略的定制。更多的是,保持其默认值,然后在特定的事务中,使用HQL的左连接抓取(left join fetch) 对其进行重载。这将通知 Hibernate在第一次查询中使用外部关联(outer join),直接得到其关联数据。 在条件查询 API中,应该调用 setFetchMode(FetchMode.JOIN)语句。

其实这并不能说明hql能够按照配置文件设置的join进行抓取,这时第二级:topic-->forum 的抓取其实已经和hql没有关系了,因为前面已经产生了另一个select方式的抓取语句。
而是对象的关联获取,假如查询message时topic是设置为延迟加载的,那么在后面获取message.topic时,如topic.forum不延迟加载,那么topic-->forum会实现配置的join方式的抓取,这个显然和hql查询没有关系。









今天只是想先说一说Hibernate Fetch的作用.大家都知道,在Hibernate里为了性能考虑,引进了lazy的概念,这里我们以Parent和Child为模型来说明。

AD:

现在越来越发现其实掌握Hibernate Fetch 并不容易,Spring用起来其实简单多了,但是在用Hibernate的时候真的是需要一定的时间积累,对一个项目组来说如果采用Hibernate最好有一个对Hibernate比较清楚的人否则碰到问题就会成为项目的风险。
我想告诉各位的是,掌握Hibernate Fetch可能比你预期的难多了,当你轻松的告诉我,Hibernate Fetch很简单的时候该是你自己多反省了. (只有一种情况例外,你是一个牛人)

好了,一个引子废话那么多,其实今天只是想先说一说Hibernate Fetch的作用.

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

  1. public class Parent implements Serializable    
  2.    
  3.         
  4.     private Long id;    
  5.    
  6.         
  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.         
  19.     private Long id;    
  20.    
  21.         
  22.     private net.foxlog.model.Parent parent;    
  23.    
  24.     //skip all getter/setter method     
  25.    
  26.   
    public class Parent implements Serializable {  
     
          
        private Long id;  
     
          
        private List childs;  
     
        //skip all getter/setter method  
     
         
    }     
     
     
     
    public class Child implements Serializable {  
     
          
        private Long id;  
     
          
        private net.foxlog.model.Parent parent;  
     
        //skip all getter/setter method  
     
    } 

在我们查询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 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);   
    Parent parent = (Parent)hibernateTemplate.execute(new HibernateCallback() {  
                public Object doInHibernate(Session session) throws HibernateException, SQLException {  
                    Query q = session.createQuery(  
                            "from Parent as parent "+  
                                    " left outer join fetch parent.childs " +  
                                    " where parent.id = :id"  
                    );  
                    q.setParameter("id",new Long(15));  
                    return (Parent)q.uniqueResult();  
                }  
     
            });  
     
            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 session.createQuery(    
  5.                         "select distinct from net.foxlog.model.Parent 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
  15.             Parent (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.       
    public void testNPlusOne() throws Exception{  
            List list = (List)hibernateTemplate.execute(new HibernateCallback() {  
                public Object doInHibernate(Session session) throws HibernateException, SQLException {  
                    Query q = session.createQuery(  
                            "select distinct p from net.foxlog.model.Parent p inner join fetch p.childs"  
                    );  
                    return q.list();  
                }  
     
            });  
     
            //((Parent)(list.get(0))).getChilds();  
            System.out.println("list size = " + list.size());  
            for(int i=0;i打印结果如下:


另外,如果用open session in view模式的话一般不用Fetch,但首先推荐Fetch,如果非用的话因为有N+1的现象,所以可以结合batch模式来改善下性能.
  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. ===parent net.foxlog.model.Parent@1401d28[id=14]    
  4. ===parent's child's length   
  5. ===parent net.foxlog.model.Parent@14e0e90[id=15]    
  6. ===parent's child's length   
  7. ===parent net.foxlog.model.Parent@62610b[id=17]    
  8. ===parent's child's length   
    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  
    list size = 3 
    ===parent = net.foxlog.model.Parent@1401d28[id=14]  
    ===parent's child's length = 1 
    ===parent = net.foxlog.model.Parent@14e0e90[id=15]  
    ===parent's child's length = 2 
    ===parent = net.foxlog.model.Parent@62610b[id=17]  
    ===parent's child's length = 3 




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值