EJB3 Persistence API標(biāo)準(zhǔn)化持久性(轉(zhuǎn))
Posted on 2008-07-11 10:26 sailor 閱讀(197) 評(píng)論(0) 編輯 收藏 所屬分類: EJB3 持久性對(duì)于大多數(shù)企業(yè)應(yīng)用程序都非常關(guān)鍵,因?yàn)樗鼈冃枰L問(wèn)關(guān)系數(shù)據(jù)庫(kù)(例如Oracle Database 10g)。如果您正在使用Java開(kāi)發(fā)應(yīng)用程序,您可能需要完成一些常規(guī)任務(wù)(例如數(shù)據(jù)庫(kù)更新和檢索),這是通過(guò)編寫(xiě)JDBC和SQL來(lái)完成的。最近幾年,幾種對(duì)象關(guān)系(O-R)映射框架(例如Oracle TopLink、JBoss Hibernate和BEA Kodo)開(kāi)始流行,因?yàn)樗鼈兒?jiǎn)化了持久性問(wèn)題,將Java開(kāi)發(fā)人員從編寫(xiě)JDBC代碼的工作中解放出來(lái),從而使他們能夠?qū)⒕杏跇I(yè)務(wù)邏輯。一些Java標(biāo)準(zhǔn)(例如EJB 2.x容器管理持久性(CMP)實(shí)體bean)也試圖解決持久性挑戰(zhàn),但是不那么成功。
雖然存在多種構(gòu)建應(yīng)用程序持久層的選擇,但是還沒(méi)有一種面向Java平臺(tái)的、在Java EE和Java SE環(huán)境下均可使用的持久性標(biāo)準(zhǔn)。好消息是EJB3 Java Persistence API (JPA)(它是EJB 3.0規(guī)范JSR-220的一部分)的出現(xiàn),它標(biāo)準(zhǔn)化了面向Java平臺(tái)的持久性API。JSR-220為O-R映射供應(yīng)商(例如TopLink、Hibernate和Kodo)以及其他領(lǐng)先的應(yīng)用服務(wù)器供應(yīng)商和JDO供應(yīng)商所廣泛接受。EJB3規(guī)范提供了一種極有吸引力的選擇,用于構(gòu)建企業(yè)Java應(yīng)用程序的持久層。
在本文中,我將介紹EJB3 Java Persistence API,我將使用一個(gè)簡(jiǎn)單的域?qū)ο竽P妥鳛槭纠?
域模型
在構(gòu)建企業(yè)應(yīng)用程序時(shí),我們首先會(huì)設(shè)計(jì)希望將其保存在數(shù)據(jù)庫(kù)中的域?qū)ο竽P停蝗缓螅c數(shù)據(jù)庫(kù)設(shè)計(jì)人員合作,確定數(shù)據(jù)庫(kù)模式。域模型表示了持久性對(duì)象或?qū)嶓w。實(shí)體可以是人、地方或事物,您存儲(chǔ)關(guān)于它們的數(shù)據(jù)。它包含數(shù)據(jù)和行為。富域模型具有所有面向?qū)ο蟮男袨樘卣鳎缋^承性和多態(tài)性。
我們的簡(jiǎn)單域模型(圖1)具有Department與Employee實(shí)體之間的雙向一對(duì)多關(guān)系。FullTime和Contractor實(shí)體繼承自Employee實(shí)體。
O-R框架和EJB3 JPA基礎(chǔ)知識(shí)
如果使用過(guò)O-R映射框架(例如Oracle TopLink)構(gòu)建應(yīng)用程序持久層,您就會(huì)注意到,每種框架都提供三種工具:
一種聲明式地執(zhí)行O-R映射的方式。這種方法(稱為O-R映射元數(shù)據(jù))允許將對(duì)象映射到一個(gè)或多個(gè)數(shù)據(jù)庫(kù)表。通常,大多數(shù)O-R框架使用XML存儲(chǔ)O-R映射元數(shù)據(jù)。
一個(gè)用于操作實(shí)體(例如,執(zhí)行CRUD操作)的API。此API允許持久化、檢索、更新或移除對(duì)象。基于API和O-R映射元數(shù)據(jù)的使用,O-R框架代表開(kāi)發(fā)人員執(zhí)行數(shù)據(jù)庫(kù)操作。此API將開(kāi)發(fā)人員從編寫(xiě)JDBC或SQL代碼以持久化域?qū)ο蟮墓ぷ髦薪夥懦鰜?lái)。
一種用于檢索對(duì)象的查詢語(yǔ)言。這是持久性最重要的方面,因?yàn)榉欠ǖ腟QL語(yǔ)句可能會(huì)降低數(shù)據(jù)庫(kù)的速度。此方法也對(duì)應(yīng)用程序屏蔽了混亂地遍布應(yīng)用程序的的專有SQL。查詢語(yǔ)言允許檢索實(shí)體或?qū)ο螅㈤_(kāi)發(fā)人員從編寫(xiě)SQL SELECT語(yǔ)句的工作中解放出來(lái)。
EJB3 Java Persistence API (JPA)提供一種標(biāo)準(zhǔn)O-R映射機(jī)制、一個(gè)執(zhí)行CRUD操作的EntityManager API以及一種擴(kuò)展EJB-QL以檢索實(shí)體的方式,從而標(biāo)準(zhǔn)化了面向Java平臺(tái)的持久性的使用。我將在后面討論這三個(gè)方面。
啟用元數(shù)據(jù)注釋
Java SE 5.0引入了元數(shù)據(jù)注釋。Java EE的所有組件(包括EJB3 JPA)大量使用元數(shù)據(jù)注釋以簡(jiǎn)化企業(yè)Java開(kāi)發(fā)。要了解關(guān)于元數(shù)據(jù)注釋的更多信息,請(qǐng)參閱Kyle Downey所著的Bridging the Gap: J2SE 5.0 Annotations。在EJB3 JPA中,注釋可以用于定義對(duì)象、關(guān)系、O-R映射和持久性上下文的注入。JPA還提供使用XML描述符來(lái)代替的選擇。我將主要介紹元數(shù)據(jù)注釋的使用,因?yàn)樗鼈兇蟠蠛?jiǎn)化了開(kāi)發(fā)。不過(guò),您可能更傾向于在生產(chǎn)部署環(huán)境中使用XML
標(biāo)準(zhǔn)化JPA中的O-R映射
定義持久對(duì)象:實(shí)體
實(shí)體是輕量級(jí)的域?qū)ο蟆M麑⑵浔4嬖陉P(guān)系數(shù)據(jù)庫(kù)中的Plain Old Java Object (POJO)。像任何POJO一樣,實(shí)體可以是抽象或具體類,它能夠擴(kuò)展另一個(gè)POJO。可以使用javax.persistence.Entity注釋將POJO標(biāo)記為實(shí)體。
以下代碼將使域模型中的Department對(duì)象成為實(shí)體:
每個(gè)實(shí)體都有一個(gè)主鍵;可以在持久字段或?qū)傩陨鲜褂肐d注釋將其標(biāo)記為主鍵。實(shí)體通過(guò)使用字段或?qū)傩裕ㄍㄟ^(guò)setter和getter方法)來(lái)保存其狀態(tài)。這取決于在哪里使用O-R映射注釋。以上示例使用基于字段的訪問(wèn);我們已經(jīng)使用了具有deptNo字段的Id注釋。要使用基于屬性的訪問(wèn),就要使用屬性標(biāo)記注釋(例如Id),如下所示:
請(qǐng)記住,對(duì)一個(gè)實(shí)體層次結(jié)構(gòu)中的所有實(shí)體,必須應(yīng)用相同的訪問(wèn)類型(字段或?qū)傩裕?br />
默認(rèn)情況下,定義在實(shí)體中的每個(gè)字段天然就是持久的;如果不希望保存字段/屬性的狀態(tài),則必須將字段/屬性定義為瞬態(tài)的,方法是使用@Transient注釋或transient修飾符標(biāo)記它。
可嵌入對(duì)象
可嵌入對(duì)象是不具有自己標(biāo)識(shí)的持久對(duì)象;它是另一個(gè)實(shí)體的一部分。例如,我們可以假定Address沒(méi)有自己的標(biāo)識(shí),且作為Employee實(shí)體的一部分存儲(chǔ)。因此,Address是可嵌入對(duì)象的候選。
可以如下所示創(chuàng)建可嵌入對(duì)象:
以下是將對(duì)象定義為目標(biāo)實(shí)體中的可嵌入對(duì)象的方法:
描述符,因?yàn)榭梢允褂盟鼈冎貙?xiě)注釋。
關(guān)系
在一個(gè)典型的域模型中,實(shí)體是彼此關(guān)聯(lián)的,或者它們相互之間存在著關(guān)系。兩個(gè)實(shí)體之間的關(guān)系可以是一對(duì)一、一對(duì)多、多對(duì)一和多對(duì)多的。這些關(guān)系可以分別使用OneToOne、OneToMany、ManyToOne或ManyToMany注釋表示。在我們的示例中,Department和Employee實(shí)體之間具有雙向OneToMany關(guān)系。
既然我們?cè)趯?shí)體中使用了基于字段的訪問(wèn),我們就在Department實(shí)體的關(guān)系字段上指定注釋,如下所示:
對(duì)于雙向關(guān)系,必須在關(guān)系的另一方指定mappedBy元素(如上),方法是指向擁有此關(guān)系的字段或?qū)傩缘拿Q。
標(biāo)準(zhǔn)化O-R映射
可以使用Java元數(shù)據(jù)注釋或XML實(shí)現(xiàn)實(shí)體的O-R映射。EJB3 JPA定義了多種用于O-R映射的注釋,例如Table、SecondaryTable、Column、JoinColumn和PrimaryKeyJoinColumn。請(qǐng)參閱EJB3 JPA規(guī)范,以獲得關(guān)于所有注釋的信息。
在我們的示例中,可以使用Table注釋定義實(shí)體被映射到的表,如下所示:
EJB3 JPA嚴(yán)重依賴于默認(rèn)設(shè)置,因此如果未定義表映射,則持久性提供程序會(huì)假定此實(shí)體被映射到與實(shí)體類同名的表(在我們的示例中是DEPARTMENT)。如果實(shí)體被映射到多個(gè)表,則可以使用SecondaryTable注釋。
可以使用Column注釋將持久字段或?qū)傩杂成涞綌?shù)據(jù)庫(kù)列,如下所示:
這里,DNAME是持久字段名稱所映射到的列的名稱。如果未使用Column注釋定義O-R映射,則持久性引擎會(huì)嘗試將其狀態(tài)保存在列中(使用與字段或?qū)傩韵嗤拿Q)。
實(shí)體繼承性
EJB3 JPA支持多種實(shí)體繼承性方法。它需要兩種類型的繼承性表映射策略:Single-table-per-entity層次結(jié)構(gòu)策略和Joined-Subclass策略。最好避免使用可選的table-per-class層次結(jié)構(gòu)。
Single-table-per-entity (SINGLE_TABLE)層次結(jié)構(gòu)策略允許將層次結(jié)構(gòu)中的所有實(shí)體映射到一個(gè)表。在我們的示例中,F(xiàn)ullTime和Contractor擴(kuò)展了Employee,所有這些都可以映射到一個(gè)名為EMP的表。換句話說(shuō),所有與Employee、FullTime和Contractor有關(guān)的數(shù)據(jù)都被存儲(chǔ)于相同的表內(nèi)。
如果使用Joined Subclass策略,則可以將公共持久數(shù)據(jù)存儲(chǔ)在超類所映射到的表中(例如Employee),并且可以為層次結(jié)構(gòu)中每個(gè)子類創(chuàng)建表,以便存儲(chǔ)特定于子類的持久字段。
必須在超類中使用Inheritance注釋,以指定繼承類型,如以下代碼所示。此示例展示了使用single-table-per-entity層次結(jié)構(gòu)策略的實(shí)體層次結(jié)構(gòu)。
每個(gè)子類必須指定用于該實(shí)體類型的鑒別器值,如下所示:
Entity Manager API:用于實(shí)體操作的標(biāo)準(zhǔn)API
javax.persistence.EntityManager管理實(shí)體生命周期,并公開(kāi)了多個(gè)在實(shí)體上執(zhí)行CRUD操作的方法。
EntityManager API在事務(wù)上下文中調(diào)用。可以在EJB容器外部(例如,從一個(gè)Web應(yīng)用程序)調(diào)用它,而無(wú)需會(huì)話bean外觀。
在執(zhí)行任何實(shí)體操作之前,必須獲取EntityManager實(shí)例。可以使用容器管理或應(yīng)用程序管理的實(shí)體管理器,可以使用JNDI查找或依賴注入來(lái)獲取EntityManager實(shí)例。正如其名稱所暗示的,Java EE容器管理著容器管理實(shí)體管理器的生命周期。它可能主要在企業(yè)Java應(yīng)用程序中使用。
可以使用PersistenceContext注入獲取容器管理實(shí)體管理器實(shí)例,如下所示:
如果使用應(yīng)用程序管理的實(shí)體管理器,則必須管理其生命周期。可以創(chuàng)建一個(gè)應(yīng)用程序管理實(shí)體管理器實(shí)例,如下所示:
然后可以使用EntityManager實(shí)例在實(shí)體上執(zhí)行CRUD操作。要關(guān)閉應(yīng)用程序管理實(shí)體管理器實(shí)例,請(qǐng)?jiān)谕瓿晒ぷ骱笳{(diào)用em.close()方法。
如前所述,必須在事務(wù)上下文中執(zhí)行涉及任何數(shù)據(jù)庫(kù)更改的實(shí)體管理器操作。
下表列出了EntityManager接口的一些用于執(zhí)行實(shí)體操作的關(guān)鍵方法。
可以使用persist()方法持久化實(shí)體實(shí)例。例如,如果想持久化Contractor實(shí)例,請(qǐng)使用以下代碼:
在持久化實(shí)體時(shí),如果此關(guān)系的CascadeType被設(shè)置為PERSIST或ALL,則任何對(duì)關(guān)聯(lián)實(shí)體的狀態(tài)更改也將被持久化。除非正在使用擴(kuò)展的持久上下文,否則實(shí)體將在事務(wù)終止后分離。合并操作允許將分離的實(shí)體實(shí)例與持久上下文合并;分離實(shí)體的狀態(tài)將與數(shù)據(jù)庫(kù)同步。這將有助于擺脫EJB 2.x中常見(jiàn)的數(shù)據(jù)傳輸對(duì)象(Data Transfer Object,DTO)反模式,因?yàn)樽鳛镻OJO的實(shí)體可以在層與層之間傳輸。惟一的要求是實(shí)體類必須實(shí)現(xiàn)java.io.Serializable接口。
查詢API
對(duì)實(shí)體的檢索是持久性的一個(gè)重要方面。使用EJB3 JPA時(shí),使用Java持久化查詢語(yǔ)言(Java Persistence Query Language,JPQL)表示查詢。JPQL是EJBQL的擴(kuò)展,它是作為EJB 2.0規(guī)范的一部分而引入的。然而,EJB3 JPA解決了EJBQL的所有局限性,并添加了許多新特性,從而成為一種功能強(qiáng)大的查詢語(yǔ)言。
JPQL較之EJBQL 2.x的改進(jìn)
以下是EJB3 JPA中的JPQL的新特性:
·簡(jiǎn)化了的查詢語(yǔ)法
·JOIN操作
·Group By和Having Clause
·子查詢
·動(dòng)態(tài)查詢
·指定參數(shù)(named parameter)
·批量更新和刪除
此外,如果希望從特定于數(shù)據(jù)庫(kù)的查詢擴(kuò)展中獲益,則必須對(duì)查詢實(shí)體使用原生(native ) SQL。
動(dòng)態(tài)查詢與指定查詢
可以使用動(dòng)態(tài)查詢或指定查詢(named query)。指定查詢隨實(shí)體存儲(chǔ),可從應(yīng)用程序重用。
要?jiǎng)?chuàng)建動(dòng)態(tài)查詢,請(qǐng)使用實(shí)體管理器接口的createQuery方法,如下所示:
如果希望將此查詢用作指定查詢,請(qǐng)?jiān)趯?shí)體中使用NamedQuery注釋,如下所示:
要執(zhí)行指定查詢,首先使用EntityManager接口上的createNamedQuery方法創(chuàng)建一個(gè)Query實(shí)例,如下所示:
指定參數(shù)
可以在EJBQL查詢中使用指定參數(shù)(named parameter)代替位置參數(shù)(positional parameter)。例如,可以將以上查詢重寫(xiě)如下:
"select e from Employee e where e.empNo > :empNo "
如果在查詢中使用指定參數(shù),則必須設(shè)置此參數(shù)如下:
打包
EJB3 JPA標(biāo)準(zhǔn)化了POJO持久性。因此,實(shí)體并不局限于EJB模塊;它們能夠打包到Web模塊、ejb-jar模塊、EAR級(jí)中的庫(kù)模塊或標(biāo)準(zhǔn)jar文件中。也可以在Java SE中使用實(shí)體。必須在包含實(shí)體的檔案文件中打包描述符(persistence.xml),如下所示:
此描述符標(biāo)識(shí)持久性提供程序、持久單元和持久單元所使用的數(shù)據(jù)源。正如其名稱所暗示的,持久單元是集中管理的實(shí)體的集合。如果有一個(gè)定義在特定模塊中的持久單元,就不需要在persistence.xml中標(biāo)識(shí)實(shí)體類;它將由持久性提供程序動(dòng)態(tài)發(fā)現(xiàn)。
參考實(shí)現(xiàn)
BEA Kodo以及Oracle TopLink的TopLink Essentials都提供了EJB3 JPA的參考實(shí)現(xiàn)。它可分別從Open JPA和GlassFish開(kāi)源項(xiàng)目中得到。
您可以在參考實(shí)現(xiàn)服務(wù)器或其他任何服從EJB3 JPA的應(yīng)用服務(wù)器上使用本文中的代碼。
EJB3 JPA工具
開(kāi)發(fā)工具確實(shí)能夠幫助構(gòu)建更好的應(yīng)用程序,而如果使用XML實(shí)現(xiàn)O-R映射,情況可能就不太妙了。Eclipse Dali O-R映射項(xiàng)目,Oracle JDeveloper 10.1.3和BEA Workshop studio之類的工具都支持EJB3 JPA。
結(jié)束語(yǔ)
EJB3 Java Persistence API標(biāo)準(zhǔn)化了面向Java平臺(tái)的持久性API。它通過(guò)使用元數(shù)據(jù)注釋和異常配置方法,簡(jiǎn)化了透明持久性的使用。多種應(yīng)用服務(wù)器已支持EJB3規(guī)范(編者注:BEA已經(jīng)發(fā)布WebLogic Server EJB 3.0 Tech Preview )。隨著Java EE 5.0和EJB 3.0規(guī)范的完成,您將很快看到許多一流的應(yīng)用服務(wù)器和持久性提供程序會(huì)實(shí)現(xiàn)EJB3 Java Persistence API。您可以使用來(lái)自GlassFish項(xiàng)目的 參考實(shí)現(xiàn) 來(lái)啟用EJB3 Persistence。
原文:http://dev.yesky.com/396/2498396.shtml
雖然存在多種構(gòu)建應(yīng)用程序持久層的選擇,但是還沒(méi)有一種面向Java平臺(tái)的、在Java EE和Java SE環(huán)境下均可使用的持久性標(biāo)準(zhǔn)。好消息是EJB3 Java Persistence API (JPA)(它是EJB 3.0規(guī)范JSR-220的一部分)的出現(xiàn),它標(biāo)準(zhǔn)化了面向Java平臺(tái)的持久性API。JSR-220為O-R映射供應(yīng)商(例如TopLink、Hibernate和Kodo)以及其他領(lǐng)先的應(yīng)用服務(wù)器供應(yīng)商和JDO供應(yīng)商所廣泛接受。EJB3規(guī)范提供了一種極有吸引力的選擇,用于構(gòu)建企業(yè)Java應(yīng)用程序的持久層。
在本文中,我將介紹EJB3 Java Persistence API,我將使用一個(gè)簡(jiǎn)單的域?qū)ο竽P妥鳛槭纠?
域模型
在構(gòu)建企業(yè)應(yīng)用程序時(shí),我們首先會(huì)設(shè)計(jì)希望將其保存在數(shù)據(jù)庫(kù)中的域?qū)ο竽P停蝗缓螅c數(shù)據(jù)庫(kù)設(shè)計(jì)人員合作,確定數(shù)據(jù)庫(kù)模式。域模型表示了持久性對(duì)象或?qū)嶓w。實(shí)體可以是人、地方或事物,您存儲(chǔ)關(guān)于它們的數(shù)據(jù)。它包含數(shù)據(jù)和行為。富域模型具有所有面向?qū)ο蟮男袨樘卣鳎缋^承性和多態(tài)性。
我們的簡(jiǎn)單域模型(圖1)具有Department與Employee實(shí)體之間的雙向一對(duì)多關(guān)系。FullTime和Contractor實(shí)體繼承自Employee實(shí)體。
![]() 圖1.示例域?qū)ο竽P? |
O-R框架和EJB3 JPA基礎(chǔ)知識(shí)
如果使用過(guò)O-R映射框架(例如Oracle TopLink)構(gòu)建應(yīng)用程序持久層,您就會(huì)注意到,每種框架都提供三種工具:
一種聲明式地執(zhí)行O-R映射的方式。這種方法(稱為O-R映射元數(shù)據(jù))允許將對(duì)象映射到一個(gè)或多個(gè)數(shù)據(jù)庫(kù)表。通常,大多數(shù)O-R框架使用XML存儲(chǔ)O-R映射元數(shù)據(jù)。
一個(gè)用于操作實(shí)體(例如,執(zhí)行CRUD操作)的API。此API允許持久化、檢索、更新或移除對(duì)象。基于API和O-R映射元數(shù)據(jù)的使用,O-R框架代表開(kāi)發(fā)人員執(zhí)行數(shù)據(jù)庫(kù)操作。此API將開(kāi)發(fā)人員從編寫(xiě)JDBC或SQL代碼以持久化域?qū)ο蟮墓ぷ髦薪夥懦鰜?lái)。
一種用于檢索對(duì)象的查詢語(yǔ)言。這是持久性最重要的方面,因?yàn)榉欠ǖ腟QL語(yǔ)句可能會(huì)降低數(shù)據(jù)庫(kù)的速度。此方法也對(duì)應(yīng)用程序屏蔽了混亂地遍布應(yīng)用程序的的專有SQL。查詢語(yǔ)言允許檢索實(shí)體或?qū)ο螅㈤_(kāi)發(fā)人員從編寫(xiě)SQL SELECT語(yǔ)句的工作中解放出來(lái)。
EJB3 Java Persistence API (JPA)提供一種標(biāo)準(zhǔn)O-R映射機(jī)制、一個(gè)執(zhí)行CRUD操作的EntityManager API以及一種擴(kuò)展EJB-QL以檢索實(shí)體的方式,從而標(biāo)準(zhǔn)化了面向Java平臺(tái)的持久性的使用。我將在后面討論這三個(gè)方面。
啟用元數(shù)據(jù)注釋
Java SE 5.0引入了元數(shù)據(jù)注釋。Java EE的所有組件(包括EJB3 JPA)大量使用元數(shù)據(jù)注釋以簡(jiǎn)化企業(yè)Java開(kāi)發(fā)。要了解關(guān)于元數(shù)據(jù)注釋的更多信息,請(qǐng)參閱Kyle Downey所著的Bridging the Gap: J2SE 5.0 Annotations。在EJB3 JPA中,注釋可以用于定義對(duì)象、關(guān)系、O-R映射和持久性上下文的注入。JPA還提供使用XML描述符來(lái)代替的選擇。我將主要介紹元數(shù)據(jù)注釋的使用,因?yàn)樗鼈兇蟠蠛?jiǎn)化了開(kāi)發(fā)。不過(guò),您可能更傾向于在生產(chǎn)部署環(huán)境中使用XML
標(biāo)準(zhǔn)化JPA中的O-R映射
定義持久對(duì)象:實(shí)體
實(shí)體是輕量級(jí)的域?qū)ο蟆M麑⑵浔4嬖陉P(guān)系數(shù)據(jù)庫(kù)中的Plain Old Java Object (POJO)。像任何POJO一樣,實(shí)體可以是抽象或具體類,它能夠擴(kuò)展另一個(gè)POJO。可以使用javax.persistence.Entity注釋將POJO標(biāo)記為實(shí)體。
以下代碼將使域模型中的Department對(duì)象成為實(shí)體:
package onjava; import java.io.Serializable; import java.util.Collection; import javax.persistence.*; @Entity @NamedQuery(name="findAllDepartment", query="select o from Department o") @Table(name="DEPT") public class Department implements Serializable { @Id @Column(nullable=false) protected Long deptNo; @Column(name="DNAME") protected String name; @Column(name="LOC") protected String location; @OneToMany(mappedBy="department") protected Collection employees; public Department() { } ... public Collection getEmployees() { return employees; } public void setEmployees(Collection employees) { this.employees = employees; } public Employee addEmployee(Employee employee) { getEmployees().add(employee); employee.setDepartment(this); return employee; } public Employee removeEmployee(Employee employee) { getEmployees().remove(employee); employee.setDepartment(null); return employee; } } |
每個(gè)實(shí)體都有一個(gè)主鍵;可以在持久字段或?qū)傩陨鲜褂肐d注釋將其標(biāo)記為主鍵。實(shí)體通過(guò)使用字段或?qū)傩裕ㄍㄟ^(guò)setter和getter方法)來(lái)保存其狀態(tài)。這取決于在哪里使用O-R映射注釋。以上示例使用基于字段的訪問(wèn);我們已經(jīng)使用了具有deptNo字段的Id注釋。要使用基于屬性的訪問(wèn),就要使用屬性標(biāo)記注釋(例如Id),如下所示:
@Id public Long getDeptNo() { return deptNo; } public void setDeptNo(Long deptNo) { this.deptNo = deptNo; } |
請(qǐng)記住,對(duì)一個(gè)實(shí)體層次結(jié)構(gòu)中的所有實(shí)體,必須應(yīng)用相同的訪問(wèn)類型(字段或?qū)傩裕?br />
默認(rèn)情況下,定義在實(shí)體中的每個(gè)字段天然就是持久的;如果不希望保存字段/屬性的狀態(tài),則必須將字段/屬性定義為瞬態(tài)的,方法是使用@Transient注釋或transient修飾符標(biāo)記它。
可嵌入對(duì)象
可嵌入對(duì)象是不具有自己標(biāo)識(shí)的持久對(duì)象;它是另一個(gè)實(shí)體的一部分。例如,我們可以假定Address沒(méi)有自己的標(biāo)識(shí),且作為Employee實(shí)體的一部分存儲(chǔ)。因此,Address是可嵌入對(duì)象的候選。
可以如下所示創(chuàng)建可嵌入對(duì)象:
@Embeddable public class Address { protected String streetAddr1; protected String streetAddr2; protected String city; protected String state; .. } |
以下是將對(duì)象定義為目標(biāo)實(shí)體中的可嵌入對(duì)象的方法:
@Entity public class Employee { @Id @GeneratedValue(strategy=GenerationType.AUTO) protected Long id; ... @Embedded protected Address address; ... } |
描述符,因?yàn)榭梢允褂盟鼈冎貙?xiě)注釋。
關(guān)系
在一個(gè)典型的域模型中,實(shí)體是彼此關(guān)聯(lián)的,或者它們相互之間存在著關(guān)系。兩個(gè)實(shí)體之間的關(guān)系可以是一對(duì)一、一對(duì)多、多對(duì)一和多對(duì)多的。這些關(guān)系可以分別使用OneToOne、OneToMany、ManyToOne或ManyToMany注釋表示。在我們的示例中,Department和Employee實(shí)體之間具有雙向OneToMany關(guān)系。
既然我們?cè)趯?shí)體中使用了基于字段的訪問(wèn),我們就在Department實(shí)體的關(guān)系字段上指定注釋,如下所示:
@OneToMany(mappedBy="department") protected Collection<Employee> employees ; |
對(duì)于雙向關(guān)系,必須在關(guān)系的另一方指定mappedBy元素(如上),方法是指向擁有此關(guān)系的字段或?qū)傩缘拿Q。
標(biāo)準(zhǔn)化O-R映射
可以使用Java元數(shù)據(jù)注釋或XML實(shí)現(xiàn)實(shí)體的O-R映射。EJB3 JPA定義了多種用于O-R映射的注釋,例如Table、SecondaryTable、Column、JoinColumn和PrimaryKeyJoinColumn。請(qǐng)參閱EJB3 JPA規(guī)范,以獲得關(guān)于所有注釋的信息。
在我們的示例中,可以使用Table注釋定義實(shí)體被映射到的表,如下所示:
@Table(name="DEPT") public class Department implements Serializable { |
EJB3 JPA嚴(yán)重依賴于默認(rèn)設(shè)置,因此如果未定義表映射,則持久性提供程序會(huì)假定此實(shí)體被映射到與實(shí)體類同名的表(在我們的示例中是DEPARTMENT)。如果實(shí)體被映射到多個(gè)表,則可以使用SecondaryTable注釋。
可以使用Column注釋將持久字段或?qū)傩杂成涞綌?shù)據(jù)庫(kù)列,如下所示:
@Column(name="DNAME") protected String name; |
這里,DNAME是持久字段名稱所映射到的列的名稱。如果未使用Column注釋定義O-R映射,則持久性引擎會(huì)嘗試將其狀態(tài)保存在列中(使用與字段或?qū)傩韵嗤拿Q)。
實(shí)體繼承性
EJB3 JPA支持多種實(shí)體繼承性方法。它需要兩種類型的繼承性表映射策略:Single-table-per-entity層次結(jié)構(gòu)策略和Joined-Subclass策略。最好避免使用可選的table-per-class層次結(jié)構(gòu)。
Single-table-per-entity (SINGLE_TABLE)層次結(jié)構(gòu)策略允許將層次結(jié)構(gòu)中的所有實(shí)體映射到一個(gè)表。在我們的示例中,F(xiàn)ullTime和Contractor擴(kuò)展了Employee,所有這些都可以映射到一個(gè)名為EMP的表。換句話說(shuō),所有與Employee、FullTime和Contractor有關(guān)的數(shù)據(jù)都被存儲(chǔ)于相同的表內(nèi)。
如果使用Joined Subclass策略,則可以將公共持久數(shù)據(jù)存儲(chǔ)在超類所映射到的表中(例如Employee),并且可以為層次結(jié)構(gòu)中每個(gè)子類創(chuàng)建表,以便存儲(chǔ)特定于子類的持久字段。
必須在超類中使用Inheritance注釋,以指定繼承類型,如以下代碼所示。此示例展示了使用single-table-per-entity層次結(jié)構(gòu)策略的實(shí)體層次結(jié)構(gòu)。
@Entity @Table(name="EMP") @Inheritance(strategy=InheritanceType.SINGLE_TABLE) @DiscriminatorColumn(name="EMPLOYEE_TYPE", discriminatorType=DiscriminatorType.STRING, length=1) public abstract class Employee implements Serializable { ... } |
每個(gè)子類必須指定用于該實(shí)體類型的鑒別器值,如下所示:
@Entity @DiscriminatorValue(value="F") public class FullTime extends Employee { @Column(name="SAL") protected Double salary; @Column(name="COMM") protected Double commission; @Column(name="DESIG") protected String designation; ... } |
Entity Manager API:用于實(shí)體操作的標(biāo)準(zhǔn)API
javax.persistence.EntityManager管理實(shí)體生命周期,并公開(kāi)了多個(gè)在實(shí)體上執(zhí)行CRUD操作的方法。
EntityManager API在事務(wù)上下文中調(diào)用。可以在EJB容器外部(例如,從一個(gè)Web應(yīng)用程序)調(diào)用它,而無(wú)需會(huì)話bean外觀。
在執(zhí)行任何實(shí)體操作之前,必須獲取EntityManager實(shí)例。可以使用容器管理或應(yīng)用程序管理的實(shí)體管理器,可以使用JNDI查找或依賴注入來(lái)獲取EntityManager實(shí)例。正如其名稱所暗示的,Java EE容器管理著容器管理實(shí)體管理器的生命周期。它可能主要在企業(yè)Java應(yīng)用程序中使用。
可以使用PersistenceContext注入獲取容器管理實(shí)體管理器實(shí)例,如下所示:
@PersistenceContext(unitName="onjava") private EntityManager em; |
如果使用應(yīng)用程序管理的實(shí)體管理器,則必須管理其生命周期。可以創(chuàng)建一個(gè)應(yīng)用程序管理實(shí)體管理器實(shí)例,如下所示:
@PersistenceUnit(unitName="onjava") private EntityManagerFactory emf; private EntityManager em = emf.createEntityManager(); |
然后可以使用EntityManager實(shí)例在實(shí)體上執(zhí)行CRUD操作。要關(guān)閉應(yīng)用程序管理實(shí)體管理器實(shí)例,請(qǐng)?jiān)谕瓿晒ぷ骱笳{(diào)用em.close()方法。
如前所述,必須在事務(wù)上下文中執(zhí)行涉及任何數(shù)據(jù)庫(kù)更改的實(shí)體管理器操作。
下表列出了EntityManager接口的一些用于執(zhí)行實(shí)體操作的關(guān)鍵方法。
方法 | 用途 |
public void persist(Object entity); | 持久化實(shí)體實(shí)例。 |
public <T> T merge(T entity); | 合并分離的實(shí)體實(shí)例。 |
public void remove(Object entity); | 移除實(shí)體實(shí)例。 |
public <T> T find(Class<T> entityClass, Object primaryKey); | 通過(guò)主鍵檢索實(shí)體實(shí)例。 |
public void flush(); | 使實(shí)體狀態(tài)與數(shù)據(jù)庫(kù)同步。 |
可以使用persist()方法持久化實(shí)體實(shí)例。例如,如果想持久化Contractor實(shí)例,請(qǐng)使用以下代碼:
@PersistenceContext(unitName="onjava") private EntityManager em; ... Contractor pte = new Contractor(); pte.setName("Nistha") pte.setHourlyRate(new Double(100.0)); em.persist(pte); |
在持久化實(shí)體時(shí),如果此關(guān)系的CascadeType被設(shè)置為PERSIST或ALL,則任何對(duì)關(guān)聯(lián)實(shí)體的狀態(tài)更改也將被持久化。除非正在使用擴(kuò)展的持久上下文,否則實(shí)體將在事務(wù)終止后分離。合并操作允許將分離的實(shí)體實(shí)例與持久上下文合并;分離實(shí)體的狀態(tài)將與數(shù)據(jù)庫(kù)同步。這將有助于擺脫EJB 2.x中常見(jiàn)的數(shù)據(jù)傳輸對(duì)象(Data Transfer Object,DTO)反模式,因?yàn)樽鳛镻OJO的實(shí)體可以在層與層之間傳輸。惟一的要求是實(shí)體類必須實(shí)現(xiàn)java.io.Serializable接口。
查詢API
對(duì)實(shí)體的檢索是持久性的一個(gè)重要方面。使用EJB3 JPA時(shí),使用Java持久化查詢語(yǔ)言(Java Persistence Query Language,JPQL)表示查詢。JPQL是EJBQL的擴(kuò)展,它是作為EJB 2.0規(guī)范的一部分而引入的。然而,EJB3 JPA解決了EJBQL的所有局限性,并添加了許多新特性,從而成為一種功能強(qiáng)大的查詢語(yǔ)言。
JPQL較之EJBQL 2.x的改進(jìn)
以下是EJB3 JPA中的JPQL的新特性:
·簡(jiǎn)化了的查詢語(yǔ)法
·JOIN操作
·Group By和Having Clause
·子查詢
·動(dòng)態(tài)查詢
·指定參數(shù)(named parameter)
·批量更新和刪除
此外,如果希望從特定于數(shù)據(jù)庫(kù)的查詢擴(kuò)展中獲益,則必須對(duì)查詢實(shí)體使用原生(native ) SQL。
動(dòng)態(tài)查詢與指定查詢
可以使用動(dòng)態(tài)查詢或指定查詢(named query)。指定查詢隨實(shí)體存儲(chǔ),可從應(yīng)用程序重用。
要?jiǎng)?chuàng)建動(dòng)態(tài)查詢,請(qǐng)使用實(shí)體管理器接口的createQuery方法,如下所示:
Query query = em.createQuery( "select e from Employee e where e.empNo > 1"); query.setParameter(1,100); return query.getResultList(); |
如果希望將此查詢用作指定查詢,請(qǐng)?jiān)趯?shí)體中使用NamedQuery注釋,如下所示:
@Entity @NamedQuery(name="findAllEmployee", query="select e from Employee e where e.empNo > 1") public abstract class Employee implements Serializable { } |
要執(zhí)行指定查詢,首先使用EntityManager接口上的createNamedQuery方法創(chuàng)建一個(gè)Query實(shí)例,如下所示:
query = em.createNamedQuery(" findAllEmployee"); query.setParameter(1,100); return query.getResultList(); |
指定參數(shù)
可以在EJBQL查詢中使用指定參數(shù)(named parameter)代替位置參數(shù)(positional parameter)。例如,可以將以上查詢重寫(xiě)如下:
"select e from Employee e where e.empNo > :empNo "
如果在查詢中使用指定參數(shù),則必須設(shè)置此參數(shù)如下:
query = em.createNamedQuery("findAllEmployee"); query.setParameter("empNo",100); return query.getResultList(); |
打包
EJB3 JPA標(biāo)準(zhǔn)化了POJO持久性。因此,實(shí)體并不局限于EJB模塊;它們能夠打包到Web模塊、ejb-jar模塊、EAR級(jí)中的庫(kù)模塊或標(biāo)準(zhǔn)jar文件中。也可以在Java SE中使用實(shí)體。必須在包含實(shí)體的檔案文件中打包描述符(persistence.xml),如下所示:
<persistence> <persistence-unit name="onjava"> <provider>oracle.toplink.essentials.PersistenceProvider</provider> <jta-data-source>jdbc/OracleDS</jta-data-source> ... </persistence-unit> </persistence> |
此描述符標(biāo)識(shí)持久性提供程序、持久單元和持久單元所使用的數(shù)據(jù)源。正如其名稱所暗示的,持久單元是集中管理的實(shí)體的集合。如果有一個(gè)定義在特定模塊中的持久單元,就不需要在persistence.xml中標(biāo)識(shí)實(shí)體類;它將由持久性提供程序動(dòng)態(tài)發(fā)現(xiàn)。
參考實(shí)現(xiàn)
BEA Kodo以及Oracle TopLink的TopLink Essentials都提供了EJB3 JPA的參考實(shí)現(xiàn)。它可分別從Open JPA和GlassFish開(kāi)源項(xiàng)目中得到。
您可以在參考實(shí)現(xiàn)服務(wù)器或其他任何服從EJB3 JPA的應(yīng)用服務(wù)器上使用本文中的代碼。
EJB3 JPA工具
開(kāi)發(fā)工具確實(shí)能夠幫助構(gòu)建更好的應(yīng)用程序,而如果使用XML實(shí)現(xiàn)O-R映射,情況可能就不太妙了。Eclipse Dali O-R映射項(xiàng)目,Oracle JDeveloper 10.1.3和BEA Workshop studio之類的工具都支持EJB3 JPA。
結(jié)束語(yǔ)
EJB3 Java Persistence API標(biāo)準(zhǔn)化了面向Java平臺(tái)的持久性API。它通過(guò)使用元數(shù)據(jù)注釋和異常配置方法,簡(jiǎn)化了透明持久性的使用。多種應(yīng)用服務(wù)器已支持EJB3規(guī)范(編者注:BEA已經(jīng)發(fā)布WebLogic Server EJB 3.0 Tech Preview )。隨著Java EE 5.0和EJB 3.0規(guī)范的完成,您將很快看到許多一流的應(yīng)用服務(wù)器和持久性提供程序會(huì)實(shí)現(xiàn)EJB3 Java Persistence API。您可以使用來(lái)自GlassFish項(xiàng)目的 參考實(shí)現(xiàn) 來(lái)啟用EJB3 Persistence。
原文:http://dev.yesky.com/396/2498396.shtml