越來(lái)越發(fā)現(xiàn)其實(shí)掌握 hibernate并不容易,Spring用起來(lái)其實(shí)簡(jiǎn)單多了,但是在用hibernate的時(shí)候真的是需要一定的時(shí)間積累,對(duì)一個(gè)項(xiàng)目組來(lái)說(shuō)如果采用hibernate最好有一個(gè)對(duì)hibernate比較清楚的人否則碰到問(wèn)題就會(huì)成為項(xiàng)目的風(fēng)險(xiǎn)。
我想告訴各位的是,掌握hibernate可能比你預(yù)期的難多了,當(dāng)你輕松的告訴我,hibernate很簡(jiǎn)單的時(shí)候該是你自己多反省了. (只有一種情況例外,你是一個(gè)牛人)

好了,一個(gè)引子廢話那么多,其實(shí)今天只是想先說(shuō)一說(shuō)hibernate里的Fetch的作用.

大家都知道,在hibernate里為了性能考慮,引進(jìn)了lazy的概念,這里我們以Parent和Child為模型來(lái)說(shuō)明,

public class Parent implements Serializable {

    
/** identifier field */
    
private Long id;

    
/** persistent field */
    
private List childs;

    
//skip all getter/setter method

  
}  



public class Child implements Serializable {

    
/** identifier field */
    
private Long id;

    
/** persistent field */
    
private net.foxlog.model.Parent parent;

    //skip all getter/setter method

}

在我們查詢(xún)Parent對(duì)象的時(shí)候,默認(rèn)只有Parent的內(nèi)容,并不包含childs的信息,如果在Parent.hbm.xml里設(shè)置lazy="false"的話才同時(shí)取出關(guān)聯(lián)的所有childs內(nèi)容.

Hibernate3多了fetch屬性,有兩種狀態(tài)select和join,設(shè)置在主表一方,如:
<set name="addresses" inverse="true" lazy="false" fetch="join">
<key>
<column name="customerid" length="10" />
</key>
<one-to-many class="ch9.SimpleOneToMany.Address" />
</set>

當(dāng)fetch=select的時(shí)候,我們查詢(xún)從表數(shù)據(jù)時(shí)候,首先會(huì)根據(jù)主表查處主表對(duì)象,然后根據(jù)主表id生成另一個(gè)select語(yǔ)句去查詢(xún)從表數(shù)據(jù),產(chǎn)生1+N的查詢(xún)效果

當(dāng)fetch=join的時(shí)候,hibernate會(huì)自動(dòng)用一條外連接語(yǔ)句同時(shí)查詢(xún)主表和從表數(shù)據(jù)

但有一點(diǎn)需要說(shuō)明的是,這個(gè)fetch屬性 只適用于使用get/load或creteria方式進(jìn)行的查詢(xún),如果使用HQL查詢(xún)則不起作用,除非在HQL中使用join操作。

問(wèn)題是我既想要hibernate默認(rèn)的性能又想要臨時(shí)的靈活性該怎么辦?  這就是fetch的功能。我們可以把fetch與lazy="true"的關(guān)系類(lèi)比為事務(wù)當(dāng)中的編程式事務(wù)與聲明式事務(wù),不太準(zhǔn)確,但是大概是這個(gè)意思。

總值,fetch就是在代碼這一層給你一個(gè)主動(dòng)抓取得機(jī)會(huì).

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去掉,就會(huì)報(bào)異常. 當(dāng)然,如果lazy="false"就不需要fetch了


有一個(gè)問(wèn)題,使用Fetch會(huì)有重復(fù)記錄的現(xiàn)象發(fā)生,我們可以理解為Fetch實(shí)際上不是為Parent服務(wù)的,而是為Child服務(wù)的.所以直接取Parent會(huì)有不匹配的問(wèn)題.



參考一下下面的這篇文章
Hibernate集合初始化

======================================================================

update:以上有些結(jié)論錯(cuò)誤,實(shí)際上在hibernate3.2.1版本下測(cè)試,可以不出現(xiàn)重復(fù)記錄,

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<list.size();i++){
            Parent p 
= (Parent)list.get(i);
            System.out.println(
"===parent = " + p);
            System.out.println(
"===parent's child's length = " + p.getChilds().size());
        }

    }


打印結(jié)果如下:
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

另外,如果用open session in view模式的話一般不用fetch,但首先推薦fetch,如果非用的話因?yàn)橛蠳+1的現(xiàn)象,所以可以結(jié)合batch模式來(lái)改善下性能.