Hinerbate單端關(guān)聯(lián)代理頗析
在Hinerbate中,對(duì)集合的延遲抓取的采用了自己的實(shí)現(xiàn)方法。但是,對(duì)于Hinerbate單端關(guān)聯(lián)的延遲抓取,則需要采用 其他不同的機(jī)制。Hinerbate單端關(guān)聯(lián)的目標(biāo)實(shí)體必須使用代理,Hihernate在運(yùn)行期二進(jìn)制級(jí)(通過(guò)優(yōu)異的CGLIB庫(kù)), 為持久對(duì)象實(shí)現(xiàn)了延遲載入代理。
默認(rèn)的,Hibernate3將會(huì)為所有的持久對(duì)象產(chǎn)生代理(在啟動(dòng)階段),然后使用他們實(shí)現(xiàn)多對(duì)一(many-to-one)關(guān)聯(lián)和一對(duì)一(one-to-one) 關(guān)聯(lián)的延遲抓取。
在映射文件中,可以通過(guò)設(shè)置proxy屬性為目標(biāo)class聲明一個(gè)接口供代理接口使用。
默認(rèn)的,Hibernate將會(huì)使用該類的一個(gè)子類。 注意:被代理的類必須實(shí)現(xiàn)一個(gè)至少包可見(jiàn)的默認(rèn)構(gòu)造函數(shù),我們建議所有的持久類都應(yīng)擁有這樣的構(gòu)造函數(shù)
在如此方式定義一個(gè)多態(tài)類的時(shí)候,有許多值得注意的常見(jiàn)性的問(wèn)題,
例如:
- <class name="Cat" proxy="Cat">
- ......
- <subclass name="DomesticCat">
- .....
- </subclass>
- </class>
首先,Cat實(shí)例永遠(yuǎn)不可以被強(qiáng)制轉(zhuǎn)換為DomesticCat, 即使它本身就是DomesticCat實(shí)例。
- Cat cat = (Cat) session.load(Cat.class, id); // instantiate a proxy (does not hit the db)
- if ( cat.isDomesticCat() ) { // hit the db to initialize the proxy
- DomesticCat dc = (DomesticCat) cat; // Error!
- ....
- }
其次,代理的“==”可能不再成立。
- Cat cat = (Cat) session.load(Cat.class, id); // instantiate a Cat proxy
- DomesticCat dc =
- (DomesticCat) session.load(DomesticCat.class, id); // acquire new DomesticCat proxy!
- System.out.println(cat==dc); // false
雖然如此,但實(shí)際情況并沒(méi)有看上去那么糟糕。雖然我們現(xiàn)在有兩個(gè)不同的引用,分別指向這兩個(gè)不同的代理對(duì)象, 但實(shí)際上,其底層應(yīng)該是同一個(gè)實(shí)例對(duì)象:
- cat.setWeight(11.0); // hit the db to initialize the proxy
- System.out.println( dc.getWeight() ); // 11.0
第三,你不能對(duì)“final類”或“具有final方法的類”使用CGLIB代理。
***,如果你的持久化對(duì)象在實(shí)例化時(shí)需要某些資源(例如,在實(shí)例化方法、默認(rèn)構(gòu)造方法中), 那么代理對(duì)象也同樣需要使用這些資源。實(shí)際上,代理類是持久化類的子類。
這些問(wèn)題都源于Java的單根繼承模型的天生限制。如果你希望避免這些問(wèn)題,那么你的每個(gè)持久化類必須實(shí)現(xiàn)一個(gè)接口, 在此接口中已經(jīng)聲明了其業(yè)務(wù)方法。然后,你需要在映射文檔中再指定這些接口。例如:
- <class name="CatImpl" proxy="Cat">
- ......
- <subclass name="DomesticCatImpl" proxy="DomesticCat">
- .....
- </subclass>
- </class>
這里CatImpl實(shí)現(xiàn)了Cat接口, DomesticCatImpl實(shí)現(xiàn)DomesticCat接口。 在load()、iterate()方法中就會(huì)返回 Cat和DomesticCat的代理對(duì)象。 (注意list()并不會(huì)返回代理對(duì)象。)
- Cat cat = (Cat) session.load(CatImpl.class, catid);
- Iterator iter = session.iterate("from CatImpl as cat where cat.name='fritz'");
- Cat fritz = (Cat) iter.next();
這里,對(duì)象之間的關(guān)系也將被延遲載入。這就意味著,你應(yīng)該將屬性聲明為Cat,而不是CatImpl。
但是,在有些方法中是不需要使用代理的。
例如:
◆equals()方法,如果持久類沒(méi)有重載equals()方法。
◆hashCode()方法,如果持久類沒(méi)有重載hashCode()方法。
◆標(biāo)志符的getter方法。
Hibernate將會(huì)識(shí)別出那些重載了equals()、或hashCode()方法的持久化類。
若選擇lazy="no-proxy"而非默認(rèn)的lazy="proxy",我們可以避免類型轉(zhuǎn)換帶來(lái)的問(wèn)題。然而,這樣我們就需要編譯期字節(jié)碼增強(qiáng),并且所有的操作都會(huì)導(dǎo)致立刻進(jìn)行代理初始化。
以上是對(duì)Hinerbate單端關(guān)聯(lián)的具體解析。