問題
隨著輕量級(jí)持久化框架的流行(如:Hibernate,JDO,JPA),領(lǐng)域?qū)ο笕〈藗鹘y(tǒng)的DTO直接作為值對(duì)象,而在這種架構(gòu)應(yīng)用的開發(fā)過程中,開發(fā)人員常會(huì)預(yù)見這樣的異常LazyInitializationException。上述問題是由于Hibernate對(duì)于領(lǐng)域?qū)ο蟮年P(guān)系域?qū)ο蟛扇×藨屑虞d策略所導(dǎo)致的(即在關(guān)系域被訪問時(shí)才真正加載創(chuàng)建這些相關(guān)對(duì)象,Hibernate提供的懶加載策略在很多時(shí)候都可以讓我的程序獲得更高的效率);由于領(lǐng)域?qū)ο笤诿摴艿臓顟B(tài)下被作為值對(duì)象傳回顯示層,而顯示層如果訪問了采用懶加載策略加載的關(guān)系域,便會(huì)導(dǎo)致LazyInitializationException異常。
為了避免這個(gè)問題,我們常常會(huì)在Façade層的業(yè)務(wù)方法中加入與特定顯示要求相綁定的返回對(duì)象初始化過程(即裝載那些被懶加載了的關(guān)系域?qū)ο螅?/span>Façade通常是我們用于控制事務(wù)邊界和完成返回的領(lǐng)域?qū)ο竺撱^的地方。
1 表現(xiàn)層是易變的這樣就會(huì)導(dǎo)致連鎖反應(yīng),大量的代碼需要維護(hù),完全違背了面向?qū)ο笤O(shè)計(jì)的原則,增加了程序的維護(hù)成本。
2 并且Façade可能用于支持多種不同的表現(xiàn)層,很難讓一個(gè)方法滿足不同的要求。
OSIV(Open Session in View)結(jié)構(gòu)是一種解決上述問題的方法,這種結(jié)構(gòu)之所以能解決上述問題關(guān)鍵就在于OSIV在表現(xiàn)層中控制Session的打開和關(guān)閉,控制事務(wù)邊界。OSIV雖然簡(jiǎn)化了應(yīng)用程序的結(jié)構(gòu),也避免了LazyInitializationException問題,但是也有很多不足:1.增加了表示層的負(fù)責(zé)度,2.在響應(yīng)返回表示層前必須提交事務(wù)。3.由于減少了封裝層次,表現(xiàn)層直接操作領(lǐng)域?qū)ο笫沟冒ㄐ阅芊矫娴母鞣N優(yōu)化更為困難。
解決方案
思考既然表示層知道要顯示的信息,即領(lǐng)域?qū)ο髴?yīng)該加載哪些相關(guān)聯(lián)的對(duì)象。那么我們何不把這個(gè)任務(wù)交給Façade的調(diào)用者(表現(xiàn)層)來完成呢?
不同的表示層可能會(huì)有不同的顯示策略,利用策略模式(GoF)我們讓表示層來注入所需的關(guān)系域加載策略實(shí)現(xiàn)。
下面便是這個(gè)想法的實(shí)現(xiàn):
示例中的領(lǐng)域模型:

圖表 1示例中領(lǐng)域?qū)ο竽P?/span>
示例中相關(guān)的組件

圖表 2示例相關(guān)組件
LazyObjectLoader:懶對(duì)象加載者接口,其定義如下
來對(duì)象加載策略接口
package org.ccsoft;
publicinterface LazyObjectLoader {
publicvoid loadLazyObjects(Object obj);
}
該接口定義了不同懶對(duì)象加載策略實(shí)現(xiàn)者要實(shí)現(xiàn)的方法,在loadLazyObjects方法中實(shí)現(xiàn)對(duì)領(lǐng)域?qū)ο?/span>obj相關(guān)的關(guān)系域?qū)ο筮M(jìn)行加載的策略。
OrderItemLoader一種加載策略的實(shí)現(xiàn),用于加載Order對(duì)象的關(guān)系域?qū)ο?/span>OrderItem對(duì)象
package org.ccsoft;
import java.io.Serializable;
publicclass OrderItem implements Serializable {
private String detail;
public String getDetail() {
returndetail;
}
publicvoid setDetail(String detail) {
this.detail = detail;
}
}
OrderMgrFacadeImp: Façade的實(shí)現(xiàn),可以使POJO也可以使Session Bean
package org.ccsoft;
publicclass OrderMgrFacadeImp implements OrderMgrFacade {
private OrderDAO orderDAO;
public OrderDAO getOrderDAO() {
returnorderDAO;
}
publicvoid setOrderDAO(OrderDAO orderDAO) {
this.orderDAO = orderDAO;
}
public Order getOrder(int orderId, LazyObjectLoader loader) {
Order order=orderDAO.getOrder(orderId);
loader.loadLazyObjects(order);
return order;
}
}
在getOrder方法中我們要求調(diào)用者提供了懶對(duì)象裝載策略的實(shí)例用于裝載那些調(diào)用者要用到的關(guān)系域?qū)ο蟆W⒁庠撌纠峭ㄟ^spring實(shí)現(xiàn)的,利用spring AOP完成事務(wù)管理,事務(wù)的邊界在Façade中方法上,當(dāng)方法執(zhí)行結(jié)束,事務(wù)業(yè)務(wù)將結(jié)束,并且Hibernate會(huì)自動(dòng)關(guān)閉session,我們看到getOrder方法中在事務(wù)結(jié)束前調(diào)用了懶對(duì)象裝載策略,從而按照調(diào)用者的要求裝載了調(diào)用者所需的關(guān)系域?qū)ο蟆?/span>
該模式的優(yōu)點(diǎn)
1 將領(lǐng)域?qū)ο蟮奶幚磉壿嬇c顯示層的顯示邏輯完全分離,提高了代碼的可維護(hù)性和可擴(kuò)展性
該模式的不足
1 要求顯示層的開發(fā)者,了解懶加載的情況
2 增加了代碼的復(fù)雜性
________________________________________________________________________
蔡 超
SCEA , SCBCD , MCSD
IBM Certified Specialist RUP
IBM Certified Solution Designer OOA&D UML v2
北京天融信軟件架構(gòu)師
SUN,Microsoft培訓(xùn)中心特邀高端教師
常年提供架構(gòu)咨詢服務(wù)
chaocai2001@yahoo.com.cn , 010-82776427