EJB
1.EJB體系結(jié)構(gòu):
包括客戶端、服務(wù)器端。
客戶端:包含了調(diào)用EJB組件特定業(yè)務(wù)所需的EJB接口(包括本地和遠(yuǎn)程EJB接口);服務(wù)器端處理對象的句柄。
服務(wù)器端:包含了EJB組件實(shí)現(xiàn)的實(shí)例;用來在客戶端和EJB組件之間進(jìn)行映射的容器代碼。
EJB客戶應(yīng)用程序使用JNDI來查找對本地接口的實(shí)例引用,通過EJB接口來調(diào)用和引用EJB組件的全部方法和屬性。
2.開發(fā)EJB程序的步驟:
開發(fā)主接口、開發(fā)組件接口、開發(fā)Bean實(shí)現(xiàn)類、編寫部署文件。
開發(fā)主接口:bean的主接口程序,命名為<bean-name>Home,繼承EJBHome,負(fù)責(zé)bean的生命周期(生成、刪除、查找bean)。只需提供主接口,類方法的實(shí)現(xiàn)由容器完成。其方法有create,remove,search等。
開發(fā)組件接口:命名為<bean_name>,繼承EJBObject,當(dāng)遠(yuǎn)程用戶調(diào)用主接口方法create時(shí),得到一個(gè)組件的遠(yuǎn)程引用,為這個(gè)bean的所有方法提供一個(gè)接口類,類和主接口的實(shí)現(xiàn)是由容器在部署時(shí)自動(dòng)生成。
開發(fā)Bean的實(shí)現(xiàn)類:命名為<bean_name>EJB,實(shí)現(xiàn)SessionBean接口。實(shí)現(xiàn)ejbCreate、ejbRemove等。
編寫部署文件:完整的bean由java類和描述其特性的ejb-jar.xml文件組成,將其打包,放在jboss中的deploy文件夾中。
3.開發(fā)和部署測試程序:
開發(fā)一個(gè)Servlet測試程序,將測試程序放在tomcat的webapps中。
4.會(huì)話Bean:
分為有狀態(tài)會(huì)話Bean和無狀態(tài)會(huì)話Bean。EJB容器通過ejb-jar.xml來判斷是否為一個(gè)SessionBean提供保存狀態(tài)的服務(wù)。
所有SessionBean的壽命周期由容器控制,Bean的客戶不擁有Bean的直接引用。部署一個(gè)EJB時(shí),容器為這個(gè)Bean分配幾個(gè)實(shí)例到Component Pooling,當(dāng)客戶請求一個(gè)Bean時(shí),J2EE服務(wù)器將一個(gè)預(yù)先實(shí)例化的Bean分配出去,在客戶的一次會(huì)話中,可以只引用一次Bean,就可以執(zhí)行這個(gè)Bean的多個(gè)方法。如果另一個(gè)客戶請求同一個(gè)Bean,容器檢查池中空閑的Bean(不在方法和事務(wù)中,如果一個(gè)客戶長時(shí)間引用一個(gè)Bean,單執(zhí)行一個(gè)方法后需等待一段事件執(zhí)行另一個(gè)方法,則這段時(shí)間也是空閑的),如果全部的實(shí)例都已用完,則會(huì)自動(dòng)生成一個(gè)新的實(shí)例放在池中,并分配給請求者。當(dāng)負(fù)載減少時(shí),池會(huì)自動(dòng)管理Bean實(shí)例的數(shù)量,將多余的實(shí)例從池中釋放。
5.有狀態(tài)會(huì)話Bean:
在客戶訪問之間保存數(shù)據(jù),在客戶引用期間維護(hù)Bean中的所有實(shí)例數(shù)據(jù)的狀態(tài)值。
有四種狀態(tài):不存在、方法現(xiàn)成、事務(wù)中方法現(xiàn)成、鈍化。
不存在:有狀態(tài)SessionBean的初始化狀態(tài)為不存在,當(dāng)客戶引用一個(gè)Bean時(shí),按照下圖中初始化一個(gè)Bean。 方法現(xiàn)成:如果客戶調(diào)用remove()方法回到不存在狀態(tài),并觸發(fā)Bean的ejbRemove()方法。如果客戶長時(shí)間不調(diào)用Bean或服務(wù)器準(zhǔn)備釋放一些內(nèi)存資源,則容器調(diào)用ejbPassivate()將這些Bean從組件池中鈍化,此時(shí)釋放分配的資源。當(dāng)請求被鈍化的Bean時(shí),容器調(diào)用ejbActivate()激活Bean,Bean轉(zhuǎn)到方法現(xiàn)成狀態(tài)時(shí)分配Bean所需的資源。
Bean本身可以管理事務(wù)(BMT Bean-Managed Transactions),也可由容器管理事務(wù)(CMT Container-Managed Transactions)。對于CMT,容器在方法開始時(shí)打開事務(wù),在方法結(jié)束時(shí)實(shí)現(xiàn)事務(wù)。通過afterBegin()、beforeCompletion()、afterCompletion()來獲取事務(wù)的各個(gè)狀態(tài),afterCompletion(true)表示事務(wù)完成,afterCompletion(false)表示事務(wù)被撤消。
setSessionContext()將對象的語境放到對象變量中,容器在結(jié)束會(huì)話Bean或自動(dòng)超時(shí)死亡之前自動(dòng)調(diào)用ejbRemove()方法,此方法中可以用來釋放資源,
6.無狀態(tài)會(huì)話Bean:
不會(huì)在客戶訪問之間保存數(shù)據(jù),不能申明實(shí)例變量,方法只能操作傳來的參數(shù)。
如果數(shù)據(jù)是數(shù)據(jù)的瞬像,則用無狀態(tài)會(huì)話Bean。弊端:本該存儲在服務(wù)器端的數(shù)據(jù)被保存在客戶中,每次調(diào)用這些數(shù)據(jù)要以參數(shù)的方式傳遞給Bean。如果數(shù)據(jù)狀態(tài)非常敏感,則不要使用無狀態(tài)會(huì)話Bean。
有兩種狀態(tài):存在、不存在。
只對客戶提供業(yè)務(wù)邏輯,服務(wù)器端不保存客戶端的任何數(shù)據(jù)狀態(tài),這些狀態(tài)被保存在客戶端。
當(dāng)客戶端不存在一個(gè)無狀態(tài)Session Bean時(shí),通過遠(yuǎn)程主接口的create()方法創(chuàng)建一個(gè)Bean,newInstance()負(fù)責(zé)實(shí)例化Bean,ejb容器調(diào)用bean類的setSessionContext()方法把運(yùn)行環(huán)境對象SessionContext傳遞給Bean,然后調(diào)用ejbCreate()方法進(jìn)行初始化和資源分配。
7.Jboss:
用Jboss來運(yùn)行EJB,將EJB的.jar文件放在jboss deploy文件夾中。這個(gè)jboss版本是Jboss2.2.1。將該EJB所有的class文件放在tomcat的webapps中。
配置ejb-jar.xml文件:
<?xml version="1.0" encoding="UTF-8"?>
<ejb-jar>
<description>This is Hello Ejb</description>
<display-name>HelloBean</display-name>
<enterprise-beans>
<session>
<ejb-name>Hello</ejb-name>
<home>hello.HelloHome</home>
<remote>hello.Hello</remote>
<ejb-class>hello.HelloEJB</ejb-class>
<session-type>Stateless</session-type>
<transaction-type>Container</transaction-type>
</session>
</enterprise-beans>
</ejb-jar>
ejb-name——是EJB的接口類名稱。這里的接口類是Hello。
home ——是主接口,包括完整的包名。
ejb-class ——是EJB的類實(shí)例,包括完整的包名。
remote ——是EJB的接口類名稱,包括完整的包名。
session-type——有兩個(gè)值Stateless、Stateful。
8.實(shí)體Bean:
用來代表底層的對象,常用來映射關(guān)系數(shù)據(jù)庫中的紀(jì)錄。關(guān)系數(shù)據(jù)庫的字段可以被一對一的映射到一個(gè)Entity Bean中,表之間的關(guān)系可以看成是Entity Bean之間的關(guān)系,一個(gè)Entity Bean實(shí)例可能會(huì)對應(yīng)表中的一條紀(jì)錄或一個(gè)查詢結(jié)果。
持久性:數(shù)據(jù)庫紀(jì)錄的任何改變也應(yīng)該被同步組件池中相關(guān)的Bean中。這一過程是持久性,是Entity Bean最重要的特征。可分為:容器管理持久性(CMP Container-Managed Persistence)和Bean管理持久性(BMP Bean-Managed Persistence)。
容器管理者:是在Bean與基礎(chǔ)數(shù)據(jù)庫表紀(jì)錄值之間負(fù)責(zé)同步工作的操作者。CMP Bean的持久性由EJB容器負(fù)責(zé),與數(shù)據(jù)庫的操作在部署EJB時(shí)由EJB部署者描述,由容器實(shí)現(xiàn)SQL操作和同步工作。BMP Bean的持久性由Bean負(fù)責(zé),由Bean開發(fā)者負(fù)責(zé)與數(shù)據(jù)庫交互的代碼部分。
9.EJB 1.1規(guī)范中的CMP:
用CMP方式編寫的Bean對于數(shù)據(jù)庫的操作是在部署時(shí)由部署者映射到實(shí)際的數(shù)據(jù)庫字段,這樣增強(qiáng)程序的移植性。不必考慮CMP Bean如何連接到數(shù)據(jù)庫,者在部署B(yǎng)ean時(shí)由部署者為CMP Bean指定一個(gè)數(shù)據(jù)庫連接池的JNDI名。
在設(shè)計(jì)一個(gè)CMP Bean時(shí),Bean被固定映射一個(gè)實(shí)體表,表中的每個(gè)指定字段被映射成Bean的一個(gè)public 型類變量,在實(shí)際開發(fā)中,只需在Bean的實(shí)現(xiàn)類中,申明這些變量,映射操作和SQL處理由部署者和容器自動(dòng)完成。
只有Entity Bean有主鍵,每個(gè)Entity Bean的實(shí)例代表一行紀(jì)錄。主鍵類型一般對應(yīng)于數(shù)據(jù)表主關(guān)鍵字類型。在Bean的實(shí)現(xiàn)類中ejbCreate(),CMP Bean返回一個(gè)NULL類型的值,BMP Bean返回一個(gè)主鍵類型對象;在Bean的遠(yuǎn)程主接口中,create()用來插入一條數(shù)據(jù),并根據(jù)ejbCreate()返回一個(gè)Bean的引用。
CMP Bean和會(huì)話Bean一樣需要設(shè)計(jì):遠(yuǎn)程主接口、組件接口和Bean的實(shí)現(xiàn)類。
10.Entity Bean的壽命周期:
客戶在調(diào)用完一個(gè)Entity Bean且釋放資源后,Entity Bean的實(shí)例仍然存在于組件池中,與映射的數(shù)據(jù)庫紀(jì)錄保持持久性。
Entity Bean的開始狀態(tài)為不存在,不引用,當(dāng)客戶直接向數(shù)據(jù)庫插入紀(jì)錄后,新的紀(jì)錄將被映射Bean的實(shí)例放到組件池中等待引用,狀態(tài)為存在,不引用,可以通過主接口的find方法查找這些對象到存在、引用狀態(tài),也可通過Home.remove()將其刪除,回到初始狀態(tài)。在初始狀態(tài)通過Home.create()可以到存在、引用狀態(tài),引用的句柄由create方法返回。只有在存在、引用時(shí)可以調(diào)用組件的業(yè)務(wù)方法。用set null可以將存在、引用狀態(tài)的Bean釋放資源,使用主接口和組件接口的remove()將刪除被映射的數(shù)據(jù)紀(jì)錄,釋放Entity Bean資源,但引用資源仍未釋放,只用set null才可以釋放引用資源。當(dāng)數(shù)據(jù)庫紀(jì)錄被其他應(yīng)用程序或進(jìn)程直接插入數(shù)據(jù)后,容器將自動(dòng)維持其持久性,在客戶端執(zhí)行完一個(gè)Entity Bean后,一定要釋放其引用的資源。
當(dāng)遠(yuǎn)程客戶調(diào)用遠(yuǎn)程主接口的create()時(shí),容器調(diào)用newInstance()來創(chuàng)建一個(gè)Bean實(shí)例,調(diào)用setEntityContext()將當(dāng)前的情境傳遞給Bean,進(jìn)入池共享階段。調(diào)用ejbCreate()、ejbPostCreate()來完全初始化Bean并進(jìn)入準(zhǔn)備階段,進(jìn)入準(zhǔn)備階段的Bean業(yè)務(wù)邏輯方法可以被客戶調(diào)用,在調(diào)用setxx或getXx時(shí),容器(CMP)或Bean(BMP)會(huì)多次調(diào)用更新(ejbStore())和提取(ejbLoad())來維護(hù)組件的持久性。
11.開發(fā)CMP Bean:
開發(fā)主接口:繼承EJBHome, 開發(fā)組件接口:繼承EJBObject,組件接口中申明的方法必須在Bean類中實(shí)現(xiàn),組件的主鍵有默認(rèn)的操作方法,所以可以不用在組件接口中申明,使用getPrimary()可以獲取組件的主鍵,返回一個(gè)Object類型,在客戶端程序中通過上溯造型成合適的類型。
開發(fā)Bean的實(shí)現(xiàn)類:實(shí)現(xiàn)了EntityBean接口,ejbLoad()從數(shù)據(jù)庫中讀取數(shù)據(jù)紀(jì)錄,ejbStore()提交當(dāng)前數(shù)據(jù)狀態(tài)到紀(jì)錄,ejbRemove()釋放實(shí)例對象并刪除相關(guān)映射的數(shù)據(jù)紀(jì)錄,setEntityContext()使當(dāng)前Bean實(shí)例訪問Bean情境,unsetEntityContext()釋放特定的情境資源。Entity Bean激活時(shí)的調(diào)用順序:ejbActivate()—ejbLoad();鈍化時(shí)的順序:ejbStore()—ejbPassivate();對于CMP的ejbCreate()返回為主關(guān)鍵字類型,由于容器來實(shí)現(xiàn),在此方法中只需關(guān)聯(lián)相關(guān)的映射字段,然后返回null。必須在類中定義與數(shù)據(jù)庫表相關(guān)聯(lián)的映射字段。
編寫部署文件:部署文件是ejb-jar.xml。
對于CMP Bean來說,當(dāng)一個(gè)Bean實(shí)例被客戶引用,容器會(huì)自動(dòng)讀取Bean的實(shí)例字段,然后,通過容器與數(shù)據(jù)庫發(fā)生關(guān)系,保存改變的數(shù)據(jù),執(zhí)行完后Bean被鈍化,并調(diào)用ejbPassivate()來通知Bean。然后又調(diào)用這個(gè)Bean時(shí),Bean先調(diào)用ejbActivate()通知Bean,Bean實(shí)例要激活,然后從數(shù)據(jù)庫中提取數(shù)據(jù),并自動(dòng)將數(shù)據(jù)值映射到Bean的實(shí)例,然后調(diào)用ejbLoad(),實(shí)例再一次被初始化,最后才開始執(zhí)行要執(zhí)行的業(yè)務(wù)方法。
12.開發(fā)BMP Bean:
當(dāng)一個(gè)Bean實(shí)例被客戶引用,并執(zhí)行一個(gè)業(yè)務(wù)方法后,容器執(zhí)行ejbStore(),由這個(gè)方法把數(shù)據(jù)保存在數(shù)據(jù)庫中,執(zhí)行完后Bean被鈍化,調(diào)用ejbPassivate()通知Bean。當(dāng)客戶又調(diào)用這個(gè)Bean的某業(yè)務(wù)方法時(shí),被鈍化的Bean又重新激活,EJB對象先調(diào)用ejbActivate()通知Bean,Bean實(shí)例要激活,然后調(diào)用Bean的ejbLoad(),此方法負(fù)責(zé)從數(shù)據(jù)庫中提取數(shù)據(jù),Bean實(shí)例被初始化,最后才執(zhí)行要執(zhí)行的業(yè)務(wù)方法。
要求所有的數(shù)據(jù)庫操作由Bean實(shí)例完成。
setXxx():來設(shè)置字段的值;getXxx():來獲取Bean字段的值;ejbCreate():由開發(fā)者實(shí)現(xiàn),返回創(chuàng)建紀(jì)錄的主鍵值;ejbLoad():以實(shí)現(xiàn)組件非持久性狀態(tài)緩存持久性信息;ejbStore():將信息從組件的非持久性狀態(tài)轉(zhuǎn)到持久性狀態(tài);ejbRemove():必須由開發(fā)者實(shí)現(xiàn);unsetEntityContext():釋放在setEntityContext()中緩存的情境資源和取得的資源;setEntityContext():設(shè)置情境資源,初始化數(shù)據(jù)庫連接對象;ejbActivate():通過情境參數(shù)設(shè)置主鍵值;ejbPassivate():取消Bean與數(shù)據(jù)庫記錄的持久性工作,進(jìn)入鈍化狀態(tài)。
開發(fā)主接口:與開發(fā)CMP Bean的主接口一樣。
開發(fā)組件接口:與開發(fā)CMP Bean的主件接口一樣。
開發(fā)Bean的實(shí)現(xiàn)類:Bean不在聲明全局的類變量,類變量的映射改教給Bean來管理。需要聲明一個(gè)EntityContext情境變量,通過這個(gè)變量的getPrimaryKey()得到保存在情境中的主關(guān)鍵字,以便在Bean激活時(shí)重新初始化Bean的數(shù)據(jù)。因?yàn)橐獙?shù)據(jù)庫直接操作,所以定義一個(gè)DataSource的對象,在Bean初始化從連接池中取得一個(gè)有效數(shù)據(jù)庫的對象。定義的Connect對象將在獲取一個(gè)數(shù)據(jù)庫連接時(shí)被引用。定義一個(gè)String類型的static變量存放一個(gè)獲得數(shù)據(jù)庫資源的JNDI名。