當(dāng)前位置:工程項(xiàng)目OA系統(tǒng) > 泛普各地 > 江西OA系統(tǒng) > 南昌OA系統(tǒng) > 南昌OA信息化
用EJB 3.0簡(jiǎn)化企業(yè)Java開(kāi)發(fā)
申請(qǐng)免費(fèi)試用、咨詢電話:400-8352-114
文章來(lái)源:泛普軟件在分為兩部分的這篇文章中,你將了解到如何使用EJB 3.0中的普通Java對(duì)象(POJO)編程模型來(lái)開(kāi)發(fā)更簡(jiǎn)單、更健壯的企業(yè)Java應(yīng)用程序??蛇\(yùn)行的示例代碼演示了EJB 3.0應(yīng)用程序中的重要元素。本文將在上篇中闡述如何使用EJB 3.0注釋來(lái)開(kāi)發(fā)松散耦合的POJO應(yīng)用程序、為可管理的POJO提供容器服務(wù)。將在下篇中討論EJB 3.0實(shí)體bean如何利用POJO和注釋來(lái)大大簡(jiǎn)化數(shù)據(jù)模型及后端的關(guān)系數(shù)據(jù)庫(kù)。
上篇:使用注釋開(kāi)發(fā)POJO服務(wù)
對(duì)開(kāi)發(fā)服務(wù)器端應(yīng)用程序而言,Java企業(yè)版本即Java EE(以前叫J2EE)是一個(gè)功能強(qiáng)大、但又過(guò)于復(fù)雜的平臺(tái)。很早以來(lái),過(guò)于復(fù)雜歷來(lái)被認(rèn)為是阻礙人們采用Java EE的一個(gè)重要因素。
但在過(guò)去的三年,Java開(kāi)放源代碼社區(qū)、Java社區(qū)組織(JCP)以及主要的Java EE開(kāi)發(fā)商都在致力于簡(jiǎn)化Java EE。譬如說(shuō),實(shí)際的應(yīng)用程序使用新的設(shè)計(jì)范例來(lái)簡(jiǎn)化Java EE的開(kāi)發(fā),譬如普通Java對(duì)象(POJO)服務(wù)、服務(wù)攔截器和依賴注入。而諸多新的工具和框架也得到了廣泛采用,用于同樣的目的,譬如Hibernate、面向方面編程(AOP)、Struts、XDoclet和Spring。
這些模式和工具讓剛?cè)腴T的開(kāi)發(fā)人員更容易上手,同時(shí)提高了經(jīng)驗(yàn)豐富的Java開(kāi)發(fā)人員的生產(chǎn)力,目前它們正在被JCP集成到下一代Java EE標(biāo)準(zhǔn)(即EJB 3.0)當(dāng)中。Java開(kāi)發(fā)人員Raghu Kodali最近開(kāi)展的一項(xiàng)調(diào)查表明,把Sun的Java EE示例應(yīng)用程序RosterApp從EJB 2.1移植到EJB 3.0可以減少50%以上的代碼。
Java注釋是EJB3.0的重要特性,它把POJO服務(wù)、POJO持久性和依賴注入聯(lián)系起來(lái),成為完整的企業(yè)中間件解決方案。本文使用了一個(gè)示例應(yīng)用程序:JBoss EJB 3.0 TrailBlazer,以演示開(kāi)發(fā)添加注釋的輕便型EJB 3.0 POJO應(yīng)用程序。TrailBlazer應(yīng)用程序多次使用EJB 3.0中的不同工具和API,實(shí)現(xiàn)了一個(gè)投資計(jì)算器。示例應(yīng)用程序在JBoss 應(yīng)用服務(wù)器4.0.3里面以非傳統(tǒng)方式運(yùn)行,完全符合最新的EJB 3.0規(guī)范(公眾預(yù)覽版)。
EJB 3.0的注釋驅(qū)動(dòng)編程模型
從開(kāi)發(fā)人員的角度來(lái)看,EJB 3.0廣泛使用Java注釋。注釋有兩個(gè)重要優(yōu)點(diǎn):它們?nèi)〈诉^(guò)多的XML配置文件,而且不需要嚴(yán)格的組件模型。
注釋與XML
基于XML的部署描述符和注釋都可以用來(lái)配置Java EE應(yīng)用程序中的服務(wù)相關(guān)屬性。兩者的區(qū)別在于:XML文件與代碼分開(kāi)處理(往往在運(yùn)行時(shí));而注釋與代碼一起編譯,而且由編譯器進(jìn)行檢查。這對(duì)開(kāi)發(fā)人員產(chǎn)生了以下這些重要影響:
● 冗長(zhǎng)性:XML配置文件以冗長(zhǎng)出名。為了配置代碼,XML文件必須從代碼地方復(fù)制許多信息,譬如類名稱和方法名稱。另一方面,Java注釋卻是代碼的一部分,不需要另外引用代碼,就可以指定配置信息。
● 健壯性:XML配置文件中的復(fù)制代碼信息帶來(lái)了多個(gè)潛在故障點(diǎn)。譬如說(shuō),如果拼錯(cuò)了XML文件中的方法名稱,應(yīng)用程序會(huì)在運(yùn)行時(shí)出錯(cuò)。換句話說(shuō),XML配置文件不如注釋來(lái)得健壯。注釋可以由編譯器來(lái)檢查,同代碼的其余部分一起處理。
● 靈活性:因?yàn)閄ML文件與代碼分開(kāi)處理,所以基于XML的配置信息不是“硬編碼”的,以后可以改動(dòng)。部署時(shí)間的靈活性對(duì)系統(tǒng)管理員來(lái)說(shuō)是一項(xiàng)很好的特性。
注釋使用簡(jiǎn)單,足以滿足大多數(shù)應(yīng)用程序的要求。XML文件比較復(fù)雜,可用來(lái)處理更高級(jí)的問(wèn)題。EJB 3.0允許通過(guò)注釋來(lái)配置大多數(shù)應(yīng)用程序的設(shè)置。EJB 3.0還支持XML文件用于取消默認(rèn)的注釋值、配置外部資源(如數(shù)據(jù)庫(kù)連接)。
POJO與嚴(yán)格組件
除了取代及簡(jiǎn)化XML描述符外,注釋還可以讓我們棄用曾困擾EJB 1.x和EJB 2.x的嚴(yán)格的組件模型。
EJB 組件是容器管理的對(duì)象。容器在運(yùn)行時(shí)操縱bean實(shí)例的行為和內(nèi)部狀態(tài)。為了讓這種行為出現(xiàn),EJB 2.1規(guī)范定義了bean必須遵守的嚴(yán)格的組件模型。每個(gè)EJB類必須從為容器提供回調(diào)鉤子(callback hook)的某個(gè)抽象類繼承而來(lái)。因?yàn)镴ava只支持單一繼承,嚴(yán)格的組件模型就限制了開(kāi)發(fā)人員使用EJB組件創(chuàng)建復(fù)雜對(duì)象結(jié)構(gòu)的能力。讀者會(huì)在本文下篇分看到,如果映射實(shí)體bean中復(fù)雜的應(yīng)用程序數(shù)據(jù),這更是個(gè)問(wèn)題。
在EJB 3.0中,所有容器服務(wù)都可以通過(guò)注釋進(jìn)行配置,并提供給應(yīng)用程序里面的任何POJO。大多數(shù)情況下,不需要特殊的組件類。
開(kāi)發(fā)松散耦合的服務(wù)對(duì)象
Java EE等企業(yè)中間件的最重要的好處之一就是,讓開(kāi)發(fā)人員可以使用松散耦合的組件來(lái)開(kāi)發(fā)應(yīng)用程序。這些組件僅僅通過(guò)已發(fā)布的業(yè)務(wù)接口來(lái)進(jìn)行耦合。因此,可在不改變應(yīng)用程序其余部分的情況下,改變組件實(shí)現(xiàn)類。這樣使應(yīng)用程序更健壯、更容易測(cè)試,而且更容易移植。EJB 3.0簡(jiǎn)化了在POJO中構(gòu)建松散耦合的業(yè)務(wù)組件。
會(huì)話bean
在EJB 3.0應(yīng)用程序中,松散耦合的服務(wù)組件通常作為會(huì)話bean來(lái)實(shí)現(xiàn)。會(huì)話bean要有一個(gè)接口(即業(yè)務(wù)接口),那樣其他應(yīng)用程序的組件就可以通過(guò)它使用其服務(wù)。下面的代碼為我們的示例投資計(jì)算器服務(wù)提供了業(yè)務(wù)接口。根據(jù)投資者開(kāi)始投資時(shí)及終止投資時(shí)的年齡、基金增長(zhǎng)率及每月儲(chǔ)蓄額,它只有一個(gè)方法來(lái)計(jì)算總的投資回報(bào)。
public interface Calculator {
public double calculate (int start, int end, double growthrate, double saving); }
會(huì)話bean類僅僅實(shí)現(xiàn)了業(yè)務(wù)接口。必須通過(guò)為其添加無(wú)狀態(tài)或者有狀態(tài)的注釋,告訴EJB 3.0容器這個(gè)POJO類是會(huì)話bean。有狀態(tài)的會(huì)話bean可以在幾個(gè)不同的服務(wù)請(qǐng)求期間保持客戶端狀態(tài)。與之相反,無(wú)狀態(tài)的會(huì)話bean的請(qǐng)求每次都是由隨機(jī)的會(huì)話bean實(shí)例來(lái)處理。其行為與原來(lái)EJB 2.1中的有狀態(tài)和無(wú)狀態(tài)的會(huì)話bean的行為相一致。EJB 3.0容器計(jì)算出什么時(shí)候?yàn)閎ean對(duì)象創(chuàng)建實(shí)例,然后通過(guò)業(yè)務(wù)接口來(lái)提供。下面是會(huì)話bean實(shí)現(xiàn)類的代碼:
@Stateless
public class CalculatorBean implements Calculator {
public double calculate (int start, int end, double growthrate, double saving) {
double tmp = Math.pow(1. + growthrate / 12., 12. * (end - start) + 1);
return saving * 12. * (tmp - 1) / growthrate; }
}
還可以為一個(gè)會(huì)話bean指定多個(gè)接口-一個(gè)用于本地客戶端,一個(gè)用于遠(yuǎn)程客戶端。只要使用@Local和@Remote注釋,就可以區(qū)別接口。下面的代碼片斷顯示了CalculatorBean會(huì)話bean同時(shí)實(shí)現(xiàn)了本地接口和遠(yuǎn)程接口。如果你沒(méi)有@Local和@Remote注釋,會(huì)話bean接口就是默認(rèn)的本地接口。
@Stateless
@Local ({Calculator.class})
@Remote ({RemoteCalculator.class})
public class CalculatorBean implements Calculator, RemoteCalculator {
public double calculate (int start, int end, double growthrate, double saving) {
double tmp = Math.pow(1. + growthrate / 12., 12. * (end - start) + 1);
return saving * 12. * (tmp - 1) / growthrate; }
public String getServerInfo () {
return "This is the JBoss EJB 3.0 TrailBlazer"; }
}
會(huì)話bean用戶通過(guò)Java命令和目錄接口(JNDI)得到bean的存根對(duì)象。由容器提供的存根對(duì)象實(shí)現(xiàn)了會(huì)話bean的業(yè)務(wù)接口。針對(duì)存根對(duì)象的所有調(diào)用都被轉(zhuǎn)向容器,并針對(duì)可管理的bean實(shí)例進(jìn)行調(diào)用。至于無(wú)狀態(tài)的會(huì)話bean,每次進(jìn)行調(diào)用時(shí),都能獲得新的存根對(duì)象。至于有狀態(tài)的會(huì)話bean,必須把存根對(duì)象緩存在客戶端上,那樣容器就知道以后每次調(diào)用時(shí)為你提供相同的的bean實(shí)例。下面的代碼片斷顯示如何調(diào)用會(huì)話bean。這里介紹獲得bean存根對(duì)象的一種更簡(jiǎn)單的方法。
InitialContext ctx = new InitialContext();
cal = (Calculator) ctx.lookup(Calculator.class.getName());
double res = cal.calculate(start, end, growthrate, saving);
會(huì)話bean的生命周期管理
為了實(shí)現(xiàn)松散耦合,應(yīng)用程序把會(huì)話bean實(shí)例的創(chuàng)建、緩存、銷毀全部交給EJB 3.0容器(即反向控制設(shè)計(jì)模式)。而應(yīng)用程序只處理業(yè)務(wù)接口。
但如果應(yīng)用程序需要對(duì)會(huì)話對(duì)象實(shí)行粒度更細(xì)的控制,該如何呢?譬如說(shuō),應(yīng)用程序可能需要在容器創(chuàng)建會(huì)話bean時(shí)執(zhí)行數(shù)據(jù)庫(kù)初始化,或者在銷毀bean時(shí)需要關(guān)閉外部連接。只要在bean類中實(shí)現(xiàn)生命周期回調(diào)方法,就能實(shí)現(xiàn)這些操作。這些方法由容器在bean生命周期的不同階段(如bean創(chuàng)建和銷毀)進(jìn)行調(diào)用。在EJB 3.0中,可以指定任何bean方法作為回調(diào),只要為其添加下列注釋。不像EJB 2.1里面,所有的回調(diào)方法都必須加以實(shí)現(xiàn),即便回調(diào)方法是空的;EJB 3.0 bean可以有好多回調(diào)方法,可以是任何方法名稱。
● @PostConstruct:bean實(shí)例創(chuàng)建后,容器立即調(diào)用添加了注釋的方法。這個(gè)注釋同時(shí)適用于有狀態(tài)和無(wú)狀態(tài)的會(huì)話bean。
● @PreDestroy:容器從對(duì)象池當(dāng)中銷毀閑置或者過(guò)期的bean實(shí)例之前,調(diào)用添加了注釋的方法。這個(gè)注釋同時(shí)適用于有狀態(tài)和無(wú)狀態(tài)的會(huì)話bean。
● @PrePassivate:如果某個(gè)有狀態(tài)的會(huì)話bean實(shí)例閑置時(shí)間過(guò)長(zhǎng),容器就會(huì)將它掛起(passivate),并把其狀態(tài)保存在緩存當(dāng)中。容器將bean實(shí)例掛起之前,調(diào)用由這個(gè)注釋作以標(biāo)記的方法。這個(gè)注釋適用于有狀態(tài)的會(huì)話bean。
● @PostActivate:如果客戶端再次使用已被掛起的的有狀態(tài)的會(huì)話bean時(shí),新的實(shí)例被創(chuàng)建,bean狀態(tài)被恢復(fù)。如果被激活的bean實(shí)例準(zhǔn)備就緒,就調(diào)用由該注釋作以標(biāo)記的方法。這個(gè)注釋只適用于有狀態(tài)的會(huì)話bean。
● @Init:這個(gè)注釋為有狀態(tài)的會(huì)話bean指定了初始化方法。它有別于@PostConstruct注釋之處在于:在有狀態(tài)的會(huì)話bean中,可以用@Init對(duì)多個(gè)方法作以標(biāo)記。不過(guò),每個(gè)bean實(shí)例只能有一個(gè)@Init方法被調(diào)用。EJB 3.0容器決定調(diào)用哪個(gè)@Init方法,具體取決于bean是如何創(chuàng)建的。@PostConstruct方法在@Init方法之后被調(diào)用。
生命周期方法的另一個(gè)有用注釋是@Remove,對(duì)有狀態(tài)的會(huì)話bean來(lái)說(shuō)更是如此。應(yīng)用程序通過(guò)存根對(duì)象調(diào)用使用@Remove標(biāo)注的方法時(shí),容器就知道在該方法執(zhí)行完畢后,把bean實(shí)例從對(duì)象池當(dāng)中移走。下面是這些生命周期方法注釋在CalculatorBean中的一個(gè)示例:
@Stateful
public class CalculatorBean implements Calculator, Serializable {
@PostConstruct
public void initialize () {
//初始化歷史記錄,并從數(shù)據(jù)庫(kù)中裝入必要數(shù)據(jù)。 }
@PreDestroy
public void exit () {
// 若有必要,把歷史記錄保存至數(shù)據(jù)庫(kù)中 }
@Remove
public void stopSession () {
// 調(diào)用該方法以通知容器,移除該bean實(shí)例、終止會(huì)話。方法體可以是空的。}
}
消息驅(qū)動(dòng)的bean
會(huì)話bean服務(wù)通過(guò)同步方法調(diào)用來(lái)提供。另一種重要的松散耦合的服務(wù)就是,由入站消息觸發(fā)的異步服務(wù),入站消息包括電子郵件或者Java消息服務(wù)(JMS)消息。EJB 3.0消息驅(qū)動(dòng)的bean(MDB)是為了處理基于消息的服務(wù)請(qǐng)求而設(shè)計(jì)的組件。
MDB類必須實(shí)現(xiàn)消息監(jiān)聽(tīng)器(MessageListener)接口。當(dāng)容器檢測(cè)到該bean的消息后,就調(diào)用onMessage()方法,并把入站消息作為調(diào)用參數(shù)傳遞。MDB會(huì)決定在OnMessage()方法中如何處理消息??梢杂米⑨寔?lái)配置這個(gè)MDB監(jiān)控哪些消息隊(duì)列。MDB部署后,容器使用注釋里面指定的配置信息。在下面的示例中,當(dāng)容器檢測(cè)到queue/mdb JMS隊(duì)列中的入站消息后,就會(huì)調(diào)用CalculatorBean MDB。MDB會(huì)解析消息,并根據(jù)消息內(nèi)容執(zhí)行投資計(jì)算。
@MessageDriven(activateConfig =
{
@ActivationConfigProperty(propertyName="destinationType", ropertyValue="javax.jms.Queue"),
@ActivationConfigProperty(propertyName="destination", propertyValue="queue/mdb")
})
public class CalculatorBean implements MessageListener {
public void onMessage (Message msg) {
try {
TextMessage tmsg = (TextMessage) msg;
Timestamp sent = new Timestamp(tmsg.getLongProperty("sent"));
StringTokenizer st = new StringTokenizer(tmsg.getText(), ",");
int start = Integer.parseInt(st.nextToken());
int end = Integer.parseInt(st.nextToken());
double growthrate = Double.parseDouble(st.nextToken());
double saving = Double.parseDouble(st.nextToken());
double result = calculate (start, end, growthrate, saving);
RecordManager.addRecord (sent, result);
} catch (Exception e) {
e.printStackTrace (); }
}
}
依賴注入
在前面一節(jié)中,介紹了如何開(kāi)發(fā)松散耦合的服務(wù)組件。然而,為了使用這些服務(wù)對(duì)象,你需要通過(guò)服務(wù)器的JNDI來(lái)查詢存根對(duì)象(用于會(huì)話bean)或者消息隊(duì)列(用于MDB)。JNDI查詢是把客戶端從實(shí)際實(shí)現(xiàn)的服務(wù)對(duì)象解除耦合的一個(gè)關(guān)鍵步驟。不過(guò),基于字符串名的普通JNDI查詢并不方便。以下是幾個(gè)原因:
● 客戶端與服務(wù)端必須就基于字符串的名字達(dá)成一致。這不是由編譯器或者任何部署時(shí)間檢查所執(zhí)行的契約。
● 已獲取的服務(wù)對(duì)象在編譯時(shí)不進(jìn)行檢查,可能會(huì)導(dǎo)致運(yùn)行時(shí)出現(xiàn)數(shù)據(jù)類型轉(zhuǎn)換錯(cuò)誤(casting error)。
● 應(yīng)用程序里面一再出現(xiàn)冗長(zhǎng)的查詢代碼,該代碼有自己的try-catch代碼塊。
EJB 3.0采用了一種簡(jiǎn)單、便利的方法,把解除耦合的服務(wù)對(duì)象和資源提供給任何POJO使用。你使用@EJB注釋,就可以把EJB存根對(duì)象注入到EJB 3.0容器管理的任何POJO中。如果對(duì)某字段變量標(biāo)以注釋,容器會(huì)在第一次訪問(wèn)之前,為該變量賦予正確的值。下面的示例顯示了如何把CalculatorBean無(wú)狀態(tài)會(huì)話bean的存根對(duì)象注入到CalculatorMDB MDB類中。
public class CalculatorMDB implements MessageListener {
@EJB Calculator cal;
// 使用cal變量
// ... ... }
如果對(duì)某個(gè)屬性的JavaBean風(fēng)格的設(shè)置方法標(biāo)以注釋,屬性第一次使用之前,容器會(huì)自動(dòng)用正確的參數(shù)調(diào)用屬性設(shè)置方法。下面的代碼片斷演示了工作過(guò)程:
public class CalculatorMDB implements MessageListener {
Calculator cal;
@EJB
public void setCal (Calculator cal) {
this.cal = cal; }
// 使用cal變量
// ... ... }
除@EJB注釋外,EJB 3.0還支持@Resource注釋注入來(lái)自JNDI的任何資源。下面的例子演示了如何注入服務(wù)器的默認(rèn)的TimerService和SessionContext對(duì)象,并且演示了如何注入來(lái)自JNDI的命名數(shù)據(jù)庫(kù)和JMS資源。
@Resource
TimerService tms;
@Resource
SessionContext ctx;
@Resource (name="DefaultDS")
DataSource myDb;
@Resource (name="ConnectionFactory")
QueueConnectionFactory factory;
@Resource (name="queue/A")
Queue queue;
此外,你還可以把容器管理的持久性管理器(即實(shí)體管理器——類似Hibernate會(huì)話對(duì)象)注入到EJB 3.0 POJO中。
為POJO提供容器服務(wù)
除了管理松散耦合的服務(wù)對(duì)象的生命周期和訪問(wèn)外,EJB 3.0容器還通過(guò)簡(jiǎn)單的注釋為可管理的POJO提供運(yùn)行時(shí)服務(wù)。
事務(wù)
最有用的容器服務(wù)可能就是事務(wù)服務(wù):萬(wàn)一應(yīng)用程序出現(xiàn)錯(cuò)誤或者異常,它可以保證數(shù)據(jù)庫(kù)的完整性。你只要為POJO方法添加注釋,即可聲明事務(wù)屬性。容器可以在適當(dāng)?shù)氖聞?wù)上下文中運(yùn)行方法。譬如說(shuō),下面的代碼聲明:容器應(yīng)當(dāng)創(chuàng)建新的事務(wù)來(lái)運(yùn)行updateExchangeRate()方法。如果該方法存在,事務(wù)就提交。實(shí)際上,從updateExchangeRate()里面被調(diào)用的所有方法也都在同樣的事務(wù)上下文中執(zhí)行,除非以其他方式進(jìn)行顯示聲明。updateExchangeRate()方法中執(zhí)行的數(shù)據(jù)庫(kù)操作要么全部成功,要么全部失敗。
@Stateless
public class CalculatorBean implements Calculator {
// ... ...
@TransactionAttribute(TransactionAttributeType.REQUIRED)
public void updateExchangeRate (double newrate) throws Exception {
// 在循環(huán)中更新數(shù)據(jù)庫(kù)。
// ... ...
//循環(huán)中的操作必須全部成功,否則數(shù)據(jù)庫(kù)根本不更新。 }
}
安全
容器還能提供驗(yàn)證用戶身份的安全服務(wù),并且可以根據(jù)用戶角色,限制對(duì)可管理的POJO的訪問(wèn)。對(duì)每個(gè)POJO類而言,你可以使用@SecurityDomain注釋指定安全域,它能告訴容器到哪里去找密碼和用戶角色列表。JBoss里面的other域表明文件是類路徑中的users.propertes和roles.properties文件。然后,對(duì)于每個(gè)方法,你可以使用安全限制注釋來(lái)指定誰(shuí)可以運(yùn)行這個(gè)方法。譬如在下面例子中,容器對(duì)所有試圖執(zhí)行addFund()方法的用戶進(jìn)行驗(yàn)證,只允許角色是AdminUser的用戶才能實(shí)際運(yùn)行。如果你沒(méi)有登錄,或者不是以管理員的身份登錄,就會(huì)引發(fā)安全異常。
@Stateless
@SecurityDomain("other")
public class CalculatorBean implements Calculator {
@RolesAllowed({"AdminUser"})
public void addFund (String name, double growthrate) {
// ... ... }
@RolesAllowed({"AdminUser"})
public void addInvestor (String name, int start, int end) {
// ... ... }
@PermitAll
public Collection < Fund > getFunds () {
// ... ... }
// ... ...
@RolesAllowed({"RegularUser"})
public double calculate (int fundId, int investorId, double saving) {
// ... ... }
}
通用攔截器
事務(wù)服務(wù)和安全服務(wù)都可以被看成是由容器管理的運(yùn)行時(shí)攔截器。容器攔截來(lái)自EJB存根對(duì)象的方法調(diào)用后,為調(diào)用添加事務(wù)上下文或者安全限制。
在EJB 3.0中,你可以自己編寫攔截器來(lái)擴(kuò)展容器服務(wù)。使用@AroundInvoke注釋,就可以把任何bean方法指定為在其他任何bean方法運(yùn)行前后執(zhí)行的攔截器方法。在下面例子中,log()方法是分析及記錄其他bean方法的執(zhí)行時(shí)間的攔截器:
@Stateful
public class CalculatorBean implements Calculator {
//被“l(fā)og()”攔截的bean方法
// ... ...
@AroundInvoke
public Object log (InvocationContext ctx) throws Exception {
String className = ctx.getBean().getClass().getName();
String methodName = ctx.getMethod().getName();
String target = className + "." + methodName + "()";
long start = System.currentTimeMillis();
System.out.println ("Invoking " + target);
try {
return ctx.proceed();
} catch(Exception e) {
throw e;
} finally {
System.out.println("Exiting " + target);
cal.setTrace(cal.getTrace() + "" +"Exiting " + target);
long time = System.currentTimeMillis() - start;
System.out.println("This method takes " + time + "ms to execute");
}
}
}
下篇:可管理的POJO持久性
在Java虛擬機(jī)(JVM)里面,所有數(shù)據(jù)都被建模,并且被封裝在樹結(jié)構(gòu)的類和對(duì)象中。然而,在后端關(guān)系數(shù)據(jù)庫(kù)中,數(shù)據(jù)被建模成關(guān)系表,它們通過(guò)共享的鍵字段相互關(guān)聯(lián)起來(lái)。同一數(shù)據(jù)卻有兩個(gè)不同的視圖,這給企業(yè)Java的開(kāi)發(fā)人員帶來(lái)了挑戰(zhàn):如果你要把數(shù)據(jù)保存到持久性數(shù)據(jù)存儲(chǔ)區(qū),或者從持久性數(shù)據(jù)存儲(chǔ)區(qū)獲取數(shù)據(jù),就必須在對(duì)象和關(guān)系表示之間來(lái)回轉(zhuǎn)換數(shù)據(jù),這個(gè)過(guò)程就叫作對(duì)象-關(guān)系映射(ORM)。在Java EE(Java企業(yè)版,以前叫J2EE)中,可以通過(guò)兩個(gè)方法來(lái)完成對(duì)象-關(guān)系映射。
● 人工方法:使用Java數(shù)據(jù)庫(kù)連接性(JDBC)直接處理持久性——這個(gè)簡(jiǎn)單的解決方法適用于簡(jiǎn)單的應(yīng)用程序。JDBC API的類緊密地按照關(guān)系數(shù)據(jù)庫(kù)里面的表、行和列進(jìn)行建模。但必須在應(yīng)用程序的內(nèi)部對(duì)象模型和JDBC對(duì)象模型之間進(jìn)行人工轉(zhuǎn)換,如果應(yīng)用程序的內(nèi)部模型已經(jīng)類似二維關(guān)系表,采用JDBC是最佳方法。
● 自動(dòng)方法:可以把ORM任務(wù)交給框架去處理??蚣芡ǔL峁┝丝梢蕴幚砣魏螖?shù)據(jù)對(duì)象的API。通過(guò)這個(gè)API,可以保存、獲取及查找數(shù)據(jù)庫(kù)??蚣茉诤笈_(tái)完成對(duì)象-關(guān)系的轉(zhuǎn)換。因?yàn)獒槍?duì)特定關(guān)系的SQL查詢不適合對(duì)象接口,ORM框架通常定義了自己的查詢語(yǔ)言,可以為當(dāng)前的關(guān)系數(shù)據(jù)庫(kù)自動(dòng)生成正確的SQL語(yǔ)句。對(duì)數(shù)據(jù)模型復(fù)雜的應(yīng)用程序而言,基于框架的方法可以節(jié)省許多時(shí)間,并且減少出錯(cuò)。
ORM 框架
EJB 實(shí)體bean是Java EE中的“官方”O(jiān)RM解決方案。不過(guò)在EJB1.x和2.x中,實(shí)體bean使用起來(lái)非常困難,這有兩個(gè)原因:
● EJB 1.x和2.x實(shí)體bean必須符合嚴(yán)格的組件模型。每個(gè)bean類必須實(shí)現(xiàn)本地接口和業(yè)務(wù)接口。它們必須從某些抽象類繼承而來(lái),還要實(shí)現(xiàn)所有方法,即便許多方法是空的。有了這樣一種嚴(yán)格的組件模型,就不可能利用EJB 1.x和2.x實(shí)體bean來(lái)構(gòu)建面向?qū)ο蟮臄?shù)據(jù)模型。
● EJB 1.x和2.x容器需要極其冗長(zhǎng)的XML配置文件把實(shí)體bean映射到關(guān)系數(shù)據(jù)庫(kù)里面的表。那些文件非常冗長(zhǎng),還容易出錯(cuò)。
簡(jiǎn)而言之,EJB 1.x和2.x實(shí)體bean是一種設(shè)計(jì)拙劣的ORM框架,既滿足不了Java數(shù)據(jù)對(duì)象模型的需求,也滿足不了關(guān)系表數(shù)據(jù)模型的需求。出于對(duì)EJB 1.x和2.x實(shí)體bean的不滿,開(kāi)發(fā)人員尋求ORM的其他方案。在實(shí)際環(huán)境中,采用開(kāi)放源代碼的Hibernate(由JBoss公司開(kāi)發(fā))和Oracle公司的TopLink是兩個(gè)最成功的Java ORM框架。Hibernate和TopLink都基于POJO:它們不依賴任何預(yù)定義的組件模型。相反,它們獲得POJO數(shù)據(jù)對(duì)象(采用簡(jiǎn)單的JavaBean格式)后,會(huì)自動(dòng)解釋如何把這些數(shù)據(jù)對(duì)象以及它們之間的關(guān)系映射到關(guān)系數(shù)據(jù)庫(kù)。通常,一個(gè)JavaBean類映射到一張數(shù)據(jù)庫(kù)表,類之間的關(guān)系通過(guò)表里面的外來(lái)鍵字段進(jìn)行映射。可以在簡(jiǎn)單、直觀的XML配置文件里面指定ORM元數(shù)據(jù),譬如與JavaBean類相對(duì)應(yīng)的表名以及與屬性相對(duì)應(yīng)的列名??梢酝ㄟ^(guò)框架中的工具類(如Hibernate中的Session類)來(lái)操作這些POJO(譬如保存、獲取及查找)。
EJB 3.0建立在 Hibernate和TopLink的思想和成功這一基礎(chǔ)上。它為Java EE提供了標(biāo)準(zhǔn)的POJO ORM框架。另外,較之現(xiàn)有的POJO持久性解決方案,EJB 3.0有兩項(xiàng)重要?jiǎng)?chuàng)新:
● EJB 3.0讓開(kāi)發(fā)人員可以直接在POJO代碼中注釋映射信息,而不是使用XML文件來(lái)指定ORM元數(shù)據(jù)。譬如說(shuō),你可以用注釋來(lái)指定與每個(gè)JavaBean屬性相對(duì)應(yīng)的關(guān)系列名。讀者會(huì)在本文后面看到更多的示例。注釋使得映射更直觀,也更容易維護(hù)。
● EJB 3.0為實(shí)體bean定義了新的存檔格式。每個(gè)存檔定義了持久性上下文,后端數(shù)據(jù)庫(kù)和ORM行為各使用獨(dú)立的一組配置。本文會(huì)在后面討論持久性上下文。
現(xiàn)在,我們不妨通過(guò)幾個(gè)簡(jiǎn)單的示例來(lái)看一下EJB 3.0是如何實(shí)現(xiàn)POJO ORM的。
映射簡(jiǎn)單對(duì)象
在EJB 3.0中,每個(gè)實(shí)體bean都是JavaBean樣式的簡(jiǎn)單類。為了告訴EJB 3.0容器這個(gè)類應(yīng)當(dāng)進(jìn)行映象以實(shí)現(xiàn)持久性,應(yīng)當(dāng)用@Entity來(lái)注釋這個(gè)類。
每個(gè)實(shí)體bean類映射到關(guān)系數(shù)據(jù)庫(kù)表。默認(rèn)情況下,表名與類名相對(duì)應(yīng)??梢允褂聾Table注釋,為該類指定另一個(gè)表名。bean類的每個(gè)JavaBean屬性映射到表中的列。默認(rèn)情況下,列名就是屬性名??梢酝ㄟ^(guò)為屬性的設(shè)置方法添加@Column注釋,來(lái)改變這種默認(rèn)關(guān)系。下面是EJB 3.0實(shí)體bean類的簡(jiǎn)單示例:
@Entity
// @Table (name="AlternativeTableName")
public class Person implements Serializable {
protected int id;
protected String name;
protected Date dateOfBirth;
public void setId (int id) {
this.id = id; }
@Id(generate = GeneratorType.AUTO)
public int getId () {
return id; }
public void setName (String name) {
this.name = name; }
// @Column (name="AlternativeColumnName")
public String getName () {
return name; }
public void setDateOfBirth (Date dateOfBirth) {
this.dateOfBirth = dateOfBirth; }
public Date getDateOfBirth () {
return dateOfBirth; }
}
容器把Person類映射到Person SQL數(shù)據(jù)庫(kù)表后,每個(gè)Person實(shí)例就是表中的一行數(shù)據(jù)。
映射簡(jiǎn)單的JavaBean類很容易。但需要映射相互關(guān)聯(lián)的對(duì)象時(shí),自動(dòng)ORM框架的優(yōu)點(diǎn)才會(huì)真正體現(xiàn)出來(lái)。本文會(huì)在后面介紹EJB 3.0是如何處理對(duì)象關(guān)系的。
關(guān)系
在數(shù)據(jù)模型中,類與類之間通常有著關(guān)系。譬如,Person對(duì)象可以與Resume對(duì)象聯(lián)系起來(lái),反之亦然(一對(duì)一關(guān)系);Person對(duì)象可以與多個(gè)CreditCard對(duì)象聯(lián)系起來(lái),而CreditCard對(duì)象只與一個(gè)Person對(duì)象相對(duì)應(yīng)(一對(duì)多關(guān)系)。多個(gè)Person對(duì)象可以與一個(gè)Address對(duì)象聯(lián)系起來(lái),而一個(gè)Address對(duì)象只對(duì)應(yīng)于一個(gè)Person對(duì)象(多對(duì)一關(guān)系)。
在對(duì)象模型中,對(duì)象引用負(fù)責(zé)處理這些關(guān)系。譬如說(shuō),Person對(duì)象可以有一個(gè)屬性(即字段)來(lái)引用Resume對(duì)象,還有另一個(gè)屬性是CreditCard對(duì)象的集合體。為了把對(duì)象之間的關(guān)系告訴EJB 3.0容器,只要在POJO中注釋這些JavaBean屬性。
@Entity
public class Person implements Serializable {
// ... ...
protected Resume resume;
protected CreditCard [] cards;
protected Address addr;
// ... ...
@OneToOne
public Resume getResume () {
return resume; }
// ... ...
@ManyToOne
// @JoinColumn (name="MyCustomId")
public Address getAddr () {
return addr; }
// ... ...
@OneToMany
public Collection < CreditCard > getCards () {
return cards; }
}
在關(guān)系數(shù)據(jù)庫(kù)中,這些關(guān)系由EJB 3.0容器使用外來(lái)鍵字段自動(dòng)重新構(gòu)建。譬如說(shuō),Person表有一個(gè)外來(lái)鍵字段,里面包含了Resume表中相應(yīng)行的主鍵。運(yùn)行時(shí),EJB 3.0容器執(zhí)行一對(duì)一的關(guān)系:它保證了Resume鍵值對(duì)Person表中的每一行來(lái)說(shuō)是惟一的。為了實(shí)現(xiàn)Resume表到Person表的雙向查詢,也可以在Resume表中定義Person屬性,并為其添加@OneToOne注釋。
Person表中還有一個(gè)外來(lái)鍵字段,里面包含了Address表中相應(yīng)行的主鍵。這種情況下,同一個(gè)Address主鍵可出現(xiàn)在多個(gè)Person行中,因?yàn)檫@是多對(duì)一的關(guān)系。至于一對(duì)多的關(guān)系,映射起來(lái)要復(fù)雜一點(diǎn),因?yàn)橥鈦?lái)鍵的列是在多對(duì)一表中的數(shù)據(jù)源里面定義的。所以在CreditCard類中,必須用@ManyToOne注釋來(lái)定義Person屬性。
上面討論的相互關(guān)系只是實(shí)體bean關(guān)系的一種類型,實(shí)體bean類之間的另一種重要關(guān)系是繼承。
繼承
面向?qū)ο笤O(shè)計(jì)的一個(gè)重要概念就是繼承。使用繼承,你可以為對(duì)象創(chuàng)建復(fù)雜的樹結(jié)構(gòu),而不需要重復(fù)代碼。譬如說(shuō),顧問(wèn)(Consultant)是提供有償咨詢服務(wù)的人。因而在我們的數(shù)據(jù)模型中,Consultant類從具有另外收費(fèi)屬性的Person類繼承而來(lái)。遺憾的是,關(guān)系數(shù)據(jù)庫(kù)里面沒(méi)有繼承這個(gè)概念。ORM框架主要依靠這兩種方法來(lái)模仿這種行為:
● 框架可以為每個(gè)類生成單獨(dú)的表。子類的表從超類的表當(dāng)中復(fù)制了所有的列。子類和超類的實(shí)例被保存在相應(yīng)的表中。
● 框架可以使用包含所有子類屬性列的一張表。兩種類的實(shí)例保存在同一張表中——超類對(duì)象的行把該類沒(méi)有的列里面的值設(shè)為空值。為了讓繼承映射更健壯,表還有“區(qū)別”列,它里面存放的標(biāo)記表明每行映射到哪個(gè)類。
EJB 3.0實(shí)體bean支持上述兩種映射策略,默認(rèn)情況下采用一張表映射策略。只要注釋指明超類,即可指定繼承策略和區(qū)別列的名字。下面是Consultant類的示例,它從Person類繼承而來(lái):
@Entity
@Inheritance(discriminatorValue="C")
@DiscriminatorColumn(name="person_type")
public class Consultant extends Person {
protected double rate;
public void setRate (double rate) {
this.rate = rate; }
public double getRate () {
return rate; }
}
在上面的例子中,容器使用默認(rèn)策略將Consultant類映射到同一張表中的Person類。如果表中的person_type列的值為C,當(dāng)前行就代表Consultant對(duì)象。否則,當(dāng)前行代表普通的Person對(duì)象。
持久性檔案
鑒于數(shù)據(jù)模型有一組添加了注釋的EJB 3.0實(shí)體bean類,就可以把它們捆綁起來(lái),部署到服務(wù)器環(huán)境中。EJB 3.0為實(shí)體bean定義了特殊的存檔文件格式,名為持久性存檔(文件后綴名為.par)。
.par文件是包括諸多實(shí)體bean類組成的一個(gè)jar文件,另外加上一個(gè)簡(jiǎn)單的配置文件:META-INF/persistence.xml。persistence.xml文件定義了持久性上下文的名稱,它告訴EJB 3.0使用哪個(gè)后端數(shù)據(jù)庫(kù)(數(shù)據(jù)源)用于這一組實(shí)體bean。persistence.xml還包含了針對(duì)特定實(shí)現(xiàn)的配置屬性。譬如說(shuō),JBoss EJB 3.0在Hibernate 3.0上面實(shí)現(xiàn)。所以你可以傳遞persistence.xml文件中的任何Hibernate配置選項(xiàng)。下面是作為示例的persistence.xml文件,專門針對(duì)JBoss和Hibernate的配置屬性有關(guān)于SQL方言和二級(jí)緩存。
< entity-manager >
< name >cal< /name >
< jta-data-source >java:/DefaultDS< /jta-data-source >
< properties >
< property name="hibernate.dialect"
value="org.hibernate.dialect.MySQLDialect" />
< property name="hibernate.cache.provider_class"
value="org.jboss.ejb3.entity.TreeCacheProviderHook"/>
< property name="hibernate.treecache.mbean.object_name"
value="jboss.cache:service=EJB3EntityTreeCache"/>
< /properties >
< /entity-manager >
實(shí)體管理器
一旦部署了實(shí)體bean, 必須通過(guò)EJB 3.0實(shí)體管理器API來(lái)訪問(wèn)及操縱它們。EJB 3.0容器為每個(gè)已部署的持久性上下文(即.par文件)提供了一個(gè)實(shí)體管理器對(duì)象??梢詮腅JB 3.0會(huì)話bean POJO,通過(guò)@PersistenceContext注釋注入實(shí)體管理器對(duì)象,并傳入上下文的名字。
@Stateless
public class ManagerBean implements Manager {
@PersistenceContext (unitName="cal")
protected EntityManager em;
// 使用“em”
// ... ...}
基本操作
為了創(chuàng)建新的數(shù)據(jù)對(duì)象,并把它保存到數(shù)據(jù)庫(kù)中,只要使用Java的new關(guān)鍵字來(lái)創(chuàng)建POJO,并把它傳遞給EntityManager.persist()方法。
Person p = new Person ();
p.setName ("A new baby");
p.setDateOfBirth (new Date ());
em.persist (p);
為了從數(shù)據(jù)庫(kù)中獲取對(duì)象,可以使用EJB 3.0查詢語(yǔ)言來(lái)搜索數(shù)據(jù)庫(kù)。下面的示例演示了如何讓Person數(shù)據(jù)庫(kù)表中的所有行作為Person Java對(duì)象的集合體來(lái)返回。
// 得到所有人
Collection < Person > persons = (Collection < Person >)em.createQuery("from Person p").getResultList();
可管理的POJO
由實(shí)體管理器保存及獲取的對(duì)象在持久性上下文中加以管理。這意味著,如果對(duì)象后來(lái)發(fā)生改變,這種改變會(huì)被自動(dòng)檢測(cè)到,并且被賦予持久性、發(fā)送到數(shù)據(jù)庫(kù)中。在下面的示例中,我們更新了可管理的POJO的屬性。改變會(huì)被EJB 3.0容器自動(dòng)檢測(cè)到,并發(fā)送到數(shù)據(jù)庫(kù)中。
Person p = em.find(Person.class, personId);
p.setName ("Another Name");
//當(dāng)前事務(wù)結(jié)束后,p被自動(dòng)更新到數(shù)據(jù)庫(kù)中。
// 沒(méi)用額外的API調(diào)用。
因?yàn)镋JB 3.0實(shí)體只是POJO,它們可以進(jìn)行序列化處理,并通過(guò)網(wǎng)絡(luò)傳遞。如果某個(gè)對(duì)象不是由容器創(chuàng)建(譬如它從網(wǎng)絡(luò)連接傳遞過(guò)來(lái),或者是遠(yuǎn)程過(guò)程調(diào)用的返回值),持久性上下文就不會(huì)管理它??梢酝ㄟ^(guò)調(diào)用EntityManager.merge()方法,把一個(gè)非管理的POJO合并到持久性上下文中。下面是把經(jīng)過(guò)反序列化的POJO合并到當(dāng)前持久性上下文中的示例。
InputStream in;
// 初始化輸入流
Person p = Util.deserialize (in);
// ... ...
em.merge (p);
// p現(xiàn)在是個(gè)可管理的對(duì)象了。p的任何改變會(huì)被自動(dòng)檢測(cè)到,并被賦予持久性。
p.setName ("Another Name");
數(shù)據(jù)庫(kù)同步
實(shí)體管理器對(duì)象用于會(huì)話bean時(shí),它與服務(wù)器的事務(wù)上下文綁在一起。服務(wù)器的事務(wù)在提交時(shí),實(shí)體管理器提交,并且把同步內(nèi)容發(fā)送給數(shù)據(jù)庫(kù)。在會(huì)話bean中,默認(rèn)情況下,服務(wù)器的事務(wù)在調(diào)用堆棧末尾處提交。當(dāng)然,也可以通過(guò)注釋為每個(gè)業(yè)務(wù)方法指定詳細(xì)的事務(wù)屬性。下面的示例演示了如何為會(huì)話bean方法聲明新的事務(wù)。
@TransactionAttribute(TransactionAttributeType.REQUIRESNEW)
public void update () {
//用這個(gè)方法更新Person對(duì)象;該方法結(jié)束后,所有更新被提交,并被刷新到數(shù)據(jù)庫(kù)中。
}
如果需要在事務(wù)提交前把更新內(nèi)容刷新到數(shù)據(jù)庫(kù)中,可以通過(guò)顯式方式調(diào)用EntityManager.flush()方法。或者可以為方法添加@FlushMode(FlushModeType.NEVER)注釋,那樣事務(wù)管理器不會(huì)在這個(gè)方法結(jié)束時(shí)(即事務(wù)結(jié)束時(shí))把更新內(nèi)容刷新到數(shù)據(jù)庫(kù)中。這種情況下,可以手動(dòng)刷新所有的數(shù)據(jù)庫(kù)更新,以實(shí)現(xiàn)最大程度的控制。
EJB 3.0 提供了一種簡(jiǎn)單、有效的框架,用于把Java POJO映射到SQL數(shù)據(jù)庫(kù)中的關(guān)系表。它所采用的明智的默認(rèn)映射策略基于Java類的結(jié)構(gòu)和命名。不過(guò)也可以改動(dòng)任何默認(rèn)設(shè)置,使用簡(jiǎn)單的一組注釋來(lái)處理復(fù)雜的對(duì)象關(guān)系。
EJB 3.0實(shí)體管理器提供了簡(jiǎn)單的API來(lái)查找及搜索來(lái)自數(shù)據(jù)庫(kù)的對(duì)象,并為其賦予持久性。每個(gè)實(shí)體管理器對(duì)象與一組映射的POJO相關(guān)聯(lián),并有自己的數(shù)據(jù)庫(kù)設(shè)置。它還可以自動(dòng)與服務(wù)器的事務(wù)管理器綁在一起。
- 1存款準(zhǔn)備金率結(jié)構(gòu)性調(diào)整還有一個(gè)優(yōu)勢(shì)
- 2IE 6中存在的安全隱患
- 3OA選型中的舍與得
- 4OA軟件:無(wú)差異 不未來(lái)
- 5家庭網(wǎng)絡(luò)構(gòu)建實(shí)用攻略
- 62013年值得關(guān)注的五大技術(shù)趨勢(shì) 微軟的創(chuàng)新
- 7選OA系統(tǒng):小投入,大改變
- 8OA選型如挑衣衫:要好看的還是合適的
- 9簡(jiǎn)述RAID級(jí)別
- 10OA觀察:決戰(zhàn)周邊化 殺回工作流
- 11無(wú)線局域網(wǎng)站點(diǎn)測(cè)量
- 12對(duì)數(shù)據(jù)倉(cāng)庫(kù)探討
- 13動(dòng)態(tài)VPN技術(shù)
- 142013年OA市場(chǎng)競(jìng)爭(zhēng)熱點(diǎn)分析
- 15南昌OA系統(tǒng)采購(gòu)申請(qǐng)單與ERP數(shù)據(jù)對(duì)接
- 16在線OA系統(tǒng)提高了考試管理的信息化水平
- 17企業(yè)服務(wù)器雙機(jī)容錯(cuò)使用方法
- 18設(shè)置VLAN提高數(shù)據(jù)效率
- 19內(nèi)網(wǎng)安全的典型的誤區(qū)
- 20信息安全沒(méi)有在十全十美的方案
- 21網(wǎng)絡(luò)騙術(shù)分析
- 22SIP的生產(chǎn)力
- 23OA軟件為單位實(shí)現(xiàn)強(qiáng)而有力的企業(yè)管控
- 24安全的數(shù)據(jù)隔離與交換系統(tǒng)
- 25同步網(wǎng)狀網(wǎng)絡(luò)提供可伸縮性
- 26四招保障企業(yè)數(shù)據(jù)安全
- 27OA選型的三大陷阱
- 28網(wǎng)絡(luò)改造重視什么
- 29遠(yuǎn)程訪問(wèn)不再頭疼
- 3030秒清除Windows系統(tǒng)所有垃圾
成都公司:成都市成華區(qū)建設(shè)南路160號(hào)1層9號(hào)
重慶公司:重慶市江北區(qū)紅旗河溝華創(chuàng)商務(wù)大廈18樓