欧美性猛交XXXX免费看蜜桃,成人网18免费韩国,亚洲国产成人精品区综合,欧美日韩一区二区三区高清不卡,亚洲综合一区二区精品久久

打開(kāi)APP
userphoto
未登錄

開(kāi)通VIP,暢享免費電子書(shū)等14項超值服

開(kāi)通VIP
將遺留 Hibernate 應用程序遷移到 OpenJPA 和 EJB 3.0 - ZDN...

將遺留 Hibernate 應用程序遷移到 OpenJPA 和 EJB 3.0

ZDNet 軟件頻道    更新時(shí)間:2007-10-06作者:ibm    來(lái)源:ibm

本文關(guān)鍵詞:   技術(shù)  程序  Hibernate  中間件  

引言

Hibernate 是開(kāi)放源代碼持久性和查詢(xún)框架,提供傳統 Java™ 對象 (POJO) 到關(guān)系數據庫表的與對象相關(guān)的映射,以及數據查詢(xún)和檢索功能。Apache OpenJPA 項目將按照 EJB 3.0 Java Persistence API 規范的定義為 POJO 實(shí)體提供類(lèi)似的開(kāi)放源代碼持久性和查詢(xún)框架。本文介紹 Enterprise JavaBeans™ (EJB) 2.1 中的通用 Hibernate 場(chǎng)景,并將它們與 OpenJPA 和 EJB 3.0 中實(shí)現的等效場(chǎng)景進(jìn)行比較。具體來(lái)說(shuō),您可以并排查看 Hibernate 應用程序源代碼、對象關(guān)系映射和配置參數,并將它們與等效的 OpenJPA 源代碼、映射和配置進(jìn)行比較。這里顯示的比較不僅使您能夠了解如何進(jìn)行這些更改,而且說(shuō)明了將使用這些通用場(chǎng)景的遺留 Hibernate 應用程序遷移到 OpenJPA 相當簡(jiǎn)單。

盡管本文重點(diǎn)介紹將遺留 Hibernate 應用程序遷移到 OpenJPA,但是如果您熟悉 Hibernate,還會(huì )發(fā)現其中的價(jià)值,并希望盡快使用新的 JPA 規范以及使用 OpenJPA 持久性提供程序進(jìn)行新的應用程序開(kāi)發(fā)。

本文假設您熟悉 Hibernate 的基本概念,并將專(zhuān)門(mén)介紹 Hibernate 3.0 實(shí)現。本文中的所有示例均在 EJB 2.1 中的 Hibernate 3 中運行過(guò),并在使用 IBM® WebSphere® Application Server V6.1 Feature Pack for EJB 3.0 的 OpenJPA 0.9.7 中運行過(guò)。

將遺留 Hibernate 應用程序遷移到 OpenJPA 的原因是多方面的。例如,Hibernate 是一個(gè)非標準的、對象關(guān)系映射和持久性管理解決方案。Hibernate 3 需要 JDK 1.3.1 或更高版本。通過(guò)對比,OpenJPA 可實(shí)現 JPA 規范,該規范是 Java 5 規范的核心部分,并且 WebSphere Application Server V6.1 Feature Pack for EJB 3.0 的實(shí)現基于該規范。有關(guān)這些產(chǎn)品的詳細信息,請參見(jiàn)參考資料。

考慮到本文的目的,JPA 表示該規范,并且 OpenJPA 表示 JPA 規范的實(shí)現。

本文沒(méi)有介紹 Hibernate 的所有特性和功能,但介紹了該領(lǐng)域中經(jīng)常使用的最佳實(shí)踐。





回頁(yè)首


遷移 Hibernate 應用程序源代碼

Java Persistence API (JPA) 是作為 EJB 3.0 規范 (JSR220) 的一部分引入的,目的是讓整個(gè) Java 社區支持標準、單一的持久 API。JPA 將采用 Hibernate、TopLink、Java Data Objects 和 Container Managed Persistence (EJB-CMP) 2.1 規范的最佳理念。

JPA 適用于 Java Platform Standard Edition (Java SE) 和 Enterprise Edition (Java EE) 環(huán)境,因為它將實(shí)體表示為 JPA 持久性提供程序(如 OpenJPA)可以管理的 POJO。關(guān)于實(shí)體的對象關(guān)系映射的元數據是使用 Java 5 注釋或在 XML 描述符中指定的。實(shí)體用于將 Java 對象持久保存到數據庫。

有許多 JPA 持久性提供程序。IBM 的 JPA 規范實(shí)現基于 Apache OpenJPA 項目。隨著(zhù)這些 JPA 持久性提供程序的發(fā)布,客戶(hù)現在可以對標準 API 進(jìn)行編碼,不必在不兼容的非標準持久性提供程序之間進(jìn)行決策。

為幫助您將遺留 Hibernate 應用程序遷移到 OpenJPA,本部分將通常使用的 Hibernate 非標準 API 與等效的 OpenJPA 標準 API 進(jìn)行了比較。本部分先比較所使用的類(lèi)和接口,然后通過(guò)常規用法場(chǎng)景比較 API。

以下各部分提供了詳細信息:

  1. 類(lèi)和接口
  2. 運行時(shí)配置
  3. 會(huì )話(huà)管理
  4. 事務(wù)管理
  5. 實(shí)體管理
  6. 分離的實(shí)體

1. 類(lèi)和接口

本文重點(diǎn)介紹 JPA javax.persistence 包,而不是 OpenJPA 實(shí)現包,因為您通常會(huì )對 JPA 標準 API 進(jìn)行編碼。

下表將通常使用的 Hibernate 類(lèi)和 OpenJPA 中的等效類(lèi)進(jìn)行了比較。所有的 Hibernate 類(lèi)都位于 org.hibernate 包中。所有的 JPA 接口(和 Persistence 類(lèi))都位于 javax.persistence 包中。JPA 接口的 OpenJPA 實(shí)現位于 org.apache.openjpa.* 包中。

org.hibernate javax.persistence 說(shuō)明
cfg.Configuration Persistence 配置會(huì )話(huà)工廠(chǎng)(在 Hibernate 中)或實(shí)體管理器工廠(chǎng)(在 OpenJPA 中)的引導類(lèi)。通常用于為 JVM 創(chuàng )建單一會(huì )話(huà)(或實(shí)體管理器)工廠(chǎng)。
SessionFactory EntityManagerFactory 提供 API 以打開(kāi) Hibernate 會(huì )話(huà)(或 OpenJPA 實(shí)體管理器),并處理用戶(hù)請求。通常,每個(gè)處理客戶(hù)機請求的線(xiàn)程都打開(kāi)一個(gè)會(huì )話(huà)(或實(shí)體管理器)。
Session EntityManager 提供 API 以便在數據庫之間存儲和加載實(shí)體。它還提供 API 以獲取事務(wù)和創(chuàng )建查詢(xún)。
Transaction EntityTransaction 提供 API 以管理事務(wù)。
Query Query 提供 API 以執行查詢(xún)。

2. 運行時(shí)配置

  • Hibernate 約定

    在 Hibernate 中,運行時(shí)配置按照以下方式進(jìn)行映射:

    • 使用靜態(tài) SessionFactory 變量。
    • 使用 Configuration#configure() 方法。
    • 使用 Configuration#buildSessionFactory() 方法。


    清單 1. Hibernate 運行時(shí)配置
                    public class ORMHelper {                private static SessionFactory sf;                protected static synchronized                SessionFactory getSessionFactory(String name) {                if (sf == null) {                sf = new Configuration().configure(name).buildSessionFactory();                }                return sf;                }                ...                }

    使用遺留 Hibernate 應用程序時(shí),您通常會(huì )發(fā)現一個(gè)單一的靜態(tài) SessionFactory 實(shí)例,該實(shí)例由 JVM 中處理客戶(hù)機請求的所有線(xiàn)程共享。Hibernate 還可以創(chuàng )建多個(gè) SessionFactory 實(shí)例,但是實(shí)際很少這樣做。

    可以通過(guò)多種方法在 Hibernate 中配置 SessionFactory。最常見(jiàn)的場(chǎng)景是調用 configure() 方法。如果沒(méi)有向 configure() 傳入名稱(chēng),它將在類(lèi)路徑的根目錄中查找 hibernate.cfg.xml。如果傳入 XML 配置文件的名稱(chēng),它將在類(lèi)路徑上查找該名稱(chēng)。

    找到 XML 配置文件后,buildSessionFactory() 方法將使用該配置文件中的元數據創(chuàng )建和初始化 SessionFactory。

    牢記以下事項:

    • 有些應用程序從 JNDI 注冊表查找 SessionFactory,而不使用靜態(tài)變量,但是在第一次查找時(shí),您仍需要調用配置和 buildSessionFactory,因此幾乎沒(méi)有什么效果,而且靜態(tài)變量是較常用的方法。
    • 您還可以使用 Configuration#setProperties() 方法以編程方式配置 Hibernate 配置參數,而不使用 configure() 方法從文件讀取這些參數,但是,較好并且頻繁使用的方法是外部化 Hibernate 屬性。

與 Hibernate 一樣,在創(chuàng )建實(shí)體管理器工廠(chǎng)時(shí),OpenJPA 也可以傳入額外屬性,但是在 XML 文件中將屬性外部化到命名的持久單元是較常用的方法。

  • OpenJPA 約定

    在 OpenJPA 中,等效運行時(shí)配置按照以下方式進(jìn)行映射:

    • 使用靜態(tài) EntityManagerFactory 變量。
    • 使用 Persistence#createEntityManagerFactory()


    清單 2. OpenJPA 運行時(shí)配置
                    public class ORMHelper {                private static EntityManagerFactory sf;                protected static synchronized                EntityManagerFactory getSessionFactory(String name) {                if (sf == null) {                sf = Persistence.createEntityManagerFactory(name);                }                return sf;                }                ...                }

    與 Hibernate 一樣,處理 JVM 中客戶(hù)機請求的所有線(xiàn)程都可以使用靜態(tài) EntityManagerFactory 實(shí)例。如果需要多個(gè)實(shí)例,還可以定義靜態(tài)映射。

    createEntityManagerFactory() 在類(lèi)路徑(該類(lèi)路徑包含的持久單元名稱(chēng)與方法調用中指定的名稱(chēng)相同)的 META-INF 文件夾中查找 persistence.xml。如果找到的 persistence.xml 使用的持久單元與給定名稱(chēng)匹配,則 createEntityManagerFactory() 使用該文件中的元數據配置 EntityManagerFactory 實(shí)例。如果沒(méi)有找到具有匹配名稱(chēng)的 persistence.xml,則引發(fā) javax.persistence.PersistenceException。

3. 會(huì )話(huà)管理

通常,應用程序收到客戶(hù)機請求時(shí),將從 SessionFactory 獲取會(huì )話(huà),并在請求結束時(shí)關(guān)閉會(huì )話(huà),其中請求可以是 HttpRequest 或對無(wú)狀態(tài)會(huì )話(huà) Bean 的調用等。會(huì )話(huà)提供處理事務(wù)和從數據庫加載實(shí)體(以及將實(shí)體存儲到數據庫)的方法。

Hibernate 應用程序通常管理該會(huì )話(huà)。為了達到目標,它們通常將會(huì )話(huà)與線(xiàn)程本地存儲關(guān)聯(lián),這樣無(wú)需將會(huì )話(huà)作為參數傳遞到需要訪(fǎng)問(wèn)它的所有方法;相反,它們可以從線(xiàn)程本地存儲中檢索它。Hibernate 3.0.1 還提供了 getCurrentSession(),但是您通常會(huì )找到顯式會(huì )話(huà)管理。

就異常而言,Hibernate 3.0 會(huì )引發(fā)未經(jīng)檢查的異?;蜻\行時(shí)異常(對于 OpenJPA 也一樣),這意味著(zhù)在方法簽名中,大多數應用程序不會(huì )引發(fā) Hibernate 異常;它們也不在自己的方法中捕獲和處理 Hibernate 異常。當然,如果需要,仍可以捕獲和處理它們。

您通常還會(huì )發(fā)現,在使用 Java SE 5 實(shí)現 OpenJPA 應用程序時(shí),已使用 Java SE 1.4 實(shí)現了大多數現有遺留 Hibernate 應用程序。

下面的示例使用 getSessionFactory() helper 方法獲取創(chuàng )建/打開(kāi)會(huì )話(huà)(或實(shí)體管理器)所需的會(huì )話(huà)工廠(chǎng)(或實(shí)體管理器工廠(chǎng))。(有關(guān) getSessionFactory() 方法的詳細信息,請參見(jiàn)運行時(shí)配置。)

  • Hibernate 約定

    在 Hibernate 中,會(huì )話(huà)管理按照以下方式進(jìn)行映射:

    • 使用 ThreadLocal 獲取當前會(huì )話(huà)。
    • 使用 SessionFactory#openSession() 打開(kāi)會(huì )話(huà)。
    • 使用 Session#isOpen() and Session#close() 關(guān)閉會(huì )話(huà)。


    清單 3. Hibernate 會(huì )話(huà)管理
                    public class ORMHelper {                private static final ThreadLocal tls = new ThreadLocal();                public static void openSession() {                Session s = (Session) tls.get();                if (s == null) {                s = getSessionFactory("test.cfg.xml").openSession();                tls.set(s);                }                }                public static Session getCurrentSession() {                return (Session) tls.get();                }                public static void closeSession() {                Session s = (Session)tls.get();                tls.set(null);                if (s != null && s.isOpen()) s.close();                }                ...                }

  • OpenJPA 約定

    在 OpenJPA 中,等效的 EntityManager 管理按照以下方式進(jìn)行映射:

    • 使用 ThreadLocal 獲取當前實(shí)體管理器。
    • 使用 EntityManagerFactory#createEntityManager() 打開(kāi)會(huì )話(huà)。
    • 使用 EntityManager#isOpen() 和 EntityManager#close() 關(guān)閉會(huì )話(huà)。


    清單 4. OpenJPA 會(huì )話(huà)管理
                    public class ORMHelper{                private static final ThreadLocal tls = new ThreadLocal();                public static void openSession() {                EntityManager s = (EntityManager) tls.get();                if (s == null) {                s = getSessionFactory("test").createEntityManager();                tls.set(s);                }                }                public static EntityManager getCurrentSession() {                return (EntityManager) tls.get();                }                public static void closeSession() {                EntityManager s = (EntityManager) tls.get();                tls.set(null);                if (s != null && s.isOpen()) s.close();                }                ...                }

4. 事務(wù)管理

Hibernate 應用程序可以運行于使用不同事務(wù)策略的環(huán)境中。應用程序可以運行于使用本地 JDBC 或全局 Java Transaction API (JTA) 事務(wù)的環(huán)境中。

使用本地 JDBC 事務(wù)是最常見(jiàn)的場(chǎng)景。如果具有異類(lèi)數據存儲(如數據庫和消息隊列),則 JTA 事務(wù)非常有用;JTA 允許您將其視為單個(gè)事務(wù)。

Hibernate 應用程序通過(guò)調用事務(wù) API 來(lái)管理自己的事務(wù)。您使用的事務(wù)策略(JDBC 或 JTA)是在 Hibernate 配置文件中設置的,所以它與應用程序無(wú)關(guān)。

  • Hibernate 約定

    在 Hibernate 中,事務(wù)管理按照以下方式進(jìn)行映射:

    • 使用 Session#beginTransaction() 開(kāi)始事務(wù)。
    • 使用 Transaction#commit() 提交事務(wù)。
    • 使用 Transaction#isActive 和 Transaction#rollback() 回滾事務(wù)。


    清單 5. Hibernate 事務(wù)管理
                    public class ORMHelper {                private static final ThreadLocal tltx = new ThreadLocal();                public static void beginTransaction() {                Transaction tx = (Transaction) tltx.get();                if (tx == null) {                tx = getCurrentSession().beginTransaction();                tltx.set(tx);                }                }                public static void commitTransaction() {                Transaction tx = (Transaction)tltx.get();                if (tx != null && tx.isActive()) tx.commit();                tltx.set(null);                }                public static void rollbackTransaction() {                Transaction tx = (Transaction)tltx.get();                tltx.set(null);                if (tx != null && tx.isActive()) tx.rollback();                }                ...                }

  • OpenJPA 約定

    在 OpenJPA 中,等效的事務(wù)管理按照以下方式進(jìn)行映射:

    • 使用 EntityManager#getTransaction() 和 EntityTransaction#begin()。
    • 使用 EntityTransaction#commit()。
    • 使用 EntityTransaction#isActive() 和 EntityTransaction#rollback()。


    清單 6. OpenJPA 事務(wù)管理
                    public class ORMHelper {                private static final ThreadLocal tltx = new ThreadLocal();                public static void beginTransaction() {                EntityTransaction tx = (EntityTransaction) tltx.get();                if (tx == null) {                tx = getCurrentSession().getTransaction();                tx.begin();                tltx.set(tx);                }                }                public static void commitTransaction() {                EntityTransaction tx = (EntityTransaction)tltx.get();                if (tx != null && tx.isActive()) tx.commit();                tltx.set(null);                }                public static void rollbackTransaction() {                EntityTransaction tx = (EntityTransaction)tltx.get();                tltx.set(null);                if (tx != null && tx.isActive()) tx.rollback();                }                ...                }

    盡管 OpenJPA 示例將事務(wù)存儲在 ThreadLocal 中,但通常僅調用 getCurrentSession().getTransaction().begin(),并在以后調用 getCurrentSession().getTransaction().commit()。因此,使用 OpenJPA,實(shí)際上不需要將事務(wù)存儲在 ThreadLocal 中。

5. 實(shí)體管理

實(shí)體管理的常見(jiàn)場(chǎng)景包括:

  • 創(chuàng )建持久對象。
  • 使用主鍵檢索持久對象。
  • 更新持久對象。
  • 刪除持久對象。

通常,這些場(chǎng)景會(huì )映射到單個(gè)用例操作,并使用分離的實(shí)體在獨立的事務(wù)中執行,但是它們還可以與連接的實(shí)體一起使用。

  • Hibernate 約定

    在 Hibernate 中,實(shí)體管理按照以下方式進(jìn)行映射:

    • 使用 Session#save 使臨時(shí)對象持久化。
    • 使用 Session#load 檢索持久對象。
    • 使用 Session#update 更新分離對象的持久狀態(tài)。(如果修改持久(連接)對象的狀態(tài),則不需要調用更新;在調用 commit() 時(shí),Hibernate 自動(dòng)將更新傳播到數據庫。)
    • 使用 Session#delete 使分離或持久對象成為臨時(shí)對象。


    清單 7. Hibernate 實(shí)體管理
                    public class ORMHelper {                ...                public static void create(Serializable obj) {                getCurrentSession().save(obj);                }                public static Object retrieve(Class clz, Serializable key) {                return getCurrentSession().load(clz, key);                }                public static void update(Serializable obj ) {                getCurrentSession().saveOrUpdate(obj);                }                public static void delete(Serializable obj ) {                getCurrentSession().delete(obj);                }                }

  • OpenJPA 約定

    在 OpenJPA 中,等效的實(shí)體管理按照以下方式進(jìn)行映射:

    • 使用 EntityManager#persist 創(chuàng )建持久實(shí)體。
    • 使用 EntityManager#find 檢索持久實(shí)體。
    • 使用 EntityManager#merge 更新分離實(shí)體。(如果使用持久(連接)實(shí)體,則無(wú)需調用 merge;OpenJPA 將更新傳播到事務(wù)末尾的數據庫。)
    • 使用 EntityManager#remove 刪除分離的或持久實(shí)體。


    清單 8. OpenJPA 實(shí)體管理
                    public class ORMHelper {                ...                public static void create( Serializable obj ) {                getCurrentSession().persist((Object) obj);                }                public static Object retrieve(Class clz, Serializable key) {                return getCurrentSession().find( clz, (Object) key );                }                public static Object update( Serializable obj ) {                return getCurrentSession().merge((Object) obj);                }                public static void delete( Serializable obj ) {                getCurrentSession().remove( (Object) obj);                }                }

    在本示例中,ORMHelper#update() 方法的簽名已更改,因為 Hibernate update() 方法將新托管的持久狀態(tài)復制到傳遞給該方法的分離或(臨時(shí))對象,所以它沒(méi)有返回值。相反,在 OpenJPA merge() 方法中,原始的分離(臨時(shí))對象未更改,返回值包含新托管的持久狀態(tài)。

    除更改 ORMHelper#update 簽名外,還必須更改調用該簽名的遺留應用程序,以便將返回值顯式分配給原始的臨時(shí)對象。

6. 分離的實(shí)體

基于分層體系結構的 Hibernate 應用程序中另一個(gè)常見(jiàn)場(chǎng)景是,將無(wú)狀態(tài)會(huì )話(huà) EJB 用作會(huì )話(huà) facade 來(lái)將“分離”實(shí)體返回給 Web 層。在此場(chǎng)景中,您將發(fā)現會(huì )話(huà) EJB 啟動(dòng)和停止事務(wù)以響應來(lái)自 Web 層的調用。

用于分層體系結構的這一模式的好處是,由于 EJB 層根據用戶(hù)交互來(lái)啟動(dòng)和停止事務(wù),因此在用戶(hù)執行某項工作時(shí),事務(wù)從不保持打開(kāi)狀態(tài)。因此,所有事務(wù)的生存時(shí)間很短,應在數秒中完成。

大多數現有 Hibernate 應用程序使用 EJB 2.1 實(shí)現會(huì )話(huà) EJB,而大多數 OpenJPA 應用程序則使用 EJB 3.0。您最初應使用資源本地實(shí)體管理器遷移到 EJB 3.0 會(huì )話(huà) Bean,這樣不需要對事務(wù)邏輯進(jìn)行更改,但您還可以從 EJB 2.1 會(huì )話(huà) Bean 使用 OpenJPA(請參見(jiàn) 通過(guò) WebSphere Application Server V6.1 利用 OpenJPA)。遷移完成后,應考慮使用 JTA 實(shí)體管理器將應用程序重構到 EJB 3.0。

對于分離實(shí)體,還應注意:如果在一個(gè)事務(wù)中檢索對象,然后在事務(wù)外部修改該分離對象,則必須調用該對象的更新才能將其再保存到數據庫。這是 Hibernate 和 OpenJPA 中常見(jiàn)的編程方法。類(lèi)似地,如果您檢索對象,并在同一事務(wù)中修改該對象,則無(wú)需調用該對象的更新,就可以將其保存到數據庫;提交事務(wù)后,該對象會(huì )自動(dòng)寫(xiě)入數據庫。此方法也是 Hibernate 和 OpenJPA 的常見(jiàn)編程方法。

  • Hibernate 約定

    在 Hibernate 中,EJB 2.1 的分離實(shí)體按照以下方式進(jìn)行映射:

    • 使用會(huì )話(huà) facade 模式包裝實(shí)體。
    • 將分離實(shí)體 (POJO) 返回到 Web 層。


    清單 9. EJB2.1 中的 Hibernate 分離實(shí)體
                    public class CustomerFacadeBean implements SessionBean, CustomerFacade{                public Customer createCustomer( Customer customerEntity ) {                ORMHelper.openSession();                try {                ORMHelper.beginTransaction();                ORMHelper.create(customerEntity);                ORMHelper.commitTransaction();                return customerEntity;                } catch (RuntimeException ex) {                ORMHelper.rollbackTransaction();                throw ex;                } finally {                ORMHelper.closeSession();                }                }                public Customer updateCustomer( Customer customerEntity ) {                ORMHelper.openSession();                try {                ORMHelper.beginTransaction();                ORMHelper.update(customerEntity);                ORMHelper.commitTransaction();                return customerEntity;                } catch (RuntimeException ex) {                ORMHelper.rollbackTransaction();                throw ex;                } finally {                ORMHelper.closeSession();                }                }                public Customer getCustomer( Long customerId ) {                ORMHelper.openSession();                try {                ORMHelper.beginTransaction();                Customer customerEntity;                customerEntity = ORMHelper.retrieve(Customer.class,customerId);                ORMHelper.commitTransaction();                return customerEntity;                } catch (RuntimeException ex) {                ORMHelper.rollbackTransaction();                throw ex;                } finally {                ORMHelper.closeSession();                }                }                public void deleteCustomer( Customer customerEntity ) {                ORMHelper.openSession();                try {                ORMHelper.beginTransaction();                ORMHelper.delete(customerEntity);                ORMHelper.commitTransaction();                } catch (RuntimeException ex) {                ORMHelper.rollbackTransaction();                Throw ex;                } finally {                ORMHelper.closeSession();                }                }                ...                }

  • OpenJPA 約定

    在 OpenJPA 中,EJB 3.0 的分離實(shí)體按照以下方式進(jìn)行映射:

    • 使用會(huì )話(huà) facade 模式包裝實(shí)體。
    • 將分離實(shí)體 (POJO) 返回到 Web 層。


    清單 10. EJB 3.0 中的 OpenJPA 分離實(shí)體
                    @Stateless                @TransactionAttribute(TransactionAttributeType.SUPPORTS)                public class CustomerFacadeBean implements CustomerFacade {                public Customer createCustomer( Customer customerEntity ) {                ORMHelper.openSession();                try {                ORMHelper.beginTransaction();                ORMHelper.create(customerEntity);                ORMHelper.commitTransaction();                return customerEntity;                } catch (RuntimeException ex) {                ORMHelper.rollbackTransaction();                throw ex;                } finally {                ORMHelper.closeSession();                }                }                public Customer updateCustomer( Customer customerEntity ) {                ORMHelper.openSession();                try {                ORMHelper.beginTransaction();                Customer returnValue;                returnValue = ORMHelper.update(customerEntity);                ORMHelper.commitTransaction();                return returnValue;                } catch (RuntimeException ex) {                ORMHelper.rollbackTransaction();                throw ex;                } finally {                ORMHelper.closeSession();                }                }                public Customer getCustomer( Long customerId ) {                ORMHelper.openSession();                try {                ORMHelper.beginTransaction();                Customer customerEntity;                customerEntity = ORMHelper.retrieve(Customer.class,customerId);                ORMHelper.commitTransaction();                return customerEntity;                } catch (RuntimeException ex) {                ORMHelper.rollbackTransaction();                throw ex;                } finally {                ORMHelper.closeSession();                }                }                public void deleteCustomer( Customer customerEntity ) {                ORMHelper.openSession();                try {                ORMHelper.beginTransaction();                ORMHelper.delete(customerEntity);                ORMHelper.commitTransaction();                } catch (RuntimeException ex) {                ORMHelper.rollbackTransaction();                throw ex;                } finally {                ORMHelper.closeSession();                }                }                }

    清單 10 中演示了必須為遷移進(jìn)行的更改。

    通過(guò)比較,EJB 3.0 SessionFacade 類(lèi)不能實(shí)現 SessionBean 類(lèi),也不能實(shí)現 SessionBean 的回調方法(setSessionContext、ejbCreate、ejbRemove、ejbActivate 和 ejbPassivate)。此外,EJB 3.0 中也不需要組件接口、home 接口和部署描述符。在 EJB 2.1 部署描述符中指定的值包括在具有 Java 5 注釋的 EJB 3.0 SessionFacade 類(lèi)中。

    也就是說(shuō),對 SessionFacade 的業(yè)務(wù)方法沒(méi)有任何更改,預期會(huì )在這里看到對 Hibernate 或 OpenJPA API 的大量調用。這是因為我們在 helper 類(lèi)中封裝了大多數 Hibernate/OpenJPA API,并且會(huì )話(huà) bean 使用了 helper 類(lèi)。當然,您的應用程序可能沒(méi)有構造為在 helper 類(lèi)中封裝 Hibernate/OpenJPA API,但是,如果并行比較對前些部分中 helper 類(lèi)進(jìn)行的更改,應該能夠確定對用于會(huì )話(huà)、事務(wù)和實(shí)體管理的 EJB 會(huì )話(huà) bean 的更改是必需的。





回頁(yè)首


遷移 Hibernate 對象關(guān)系映射

Hibernate 對象關(guān)系映射可以在啟動(dòng)時(shí)加載的 XML 映射文件集中定義??梢灾苯邮褂眠@些映射文件或從嵌入源代碼的 javadoc 樣式注釋中生成。在 Hibernate 的較新版本中,還可以通過(guò) Java 5 注釋定義對象關(guān)系映射。

可以在 XML 映射文件集中定義 OpenJPA 對象關(guān)系映射,或者通過(guò)直接嵌入代碼的 Java 5 注釋定義它們,該對象關(guān)系映射完全不需要映射文件。

在開(kāi)發(fā)中,大多數遺留 Hibernate 應用程序使用 XML 映射文件,而大多數 OpenJPA 應用程序使用 Java 5 注釋?zhuān)谏a(chǎn)中則將它們移動(dòng)到 XML,這樣對映射的簡(jiǎn)單更改不需要您修改源代碼和重新構建。

由于 XML 對 Hibernate 很常見(jiàn),并且在 OpenJPA 中通常用于生產(chǎn),所以在本部分中對映射使用 XML。為幫助了解對象模型 (POJO) 中哪些內容是必需的,哪些不是必需的,還包括了相應的基礎代碼(不包括注釋?zhuān)?/p>

如果遺留 Hibernate 應用程序不使用映射文件(例如,使用 javadoc 樣式的注釋或 Java 5 注釋?zhuān)?,則您仍應能夠基于本部分中的信息得出更改,并將應用程序遷移到 OpenJPA。另一方面,如果希望將 Java 5 注釋與 OpenJPA 一起使用,附錄提供了這樣的示例。

還可以使用其他方法在 Java 對象和關(guān)系表之間進(jìn)行映射。本部分將介紹在企業(yè)應用程序中出現的通用場(chǎng)景,其中包括:

  1. 繼承
  2. 關(guān)系
  3. 延遲初始化
  4. 對象標識
  5. 樂(lè )觀(guān)鎖定

1. 繼承

企業(yè)應用程序的數據模型通常有多個(gè)位置,類(lèi)之間的一般化/專(zhuān)業(yè)化在這里提供重要的重用機會(huì )。Hibernate 和 OpenJPA 都支持可以在關(guān)系表中建模繼承的三種不同方法。我們將討論其中的兩項,我們認為這兩項是最常見(jiàn)的場(chǎng)景:

  1. 單個(gè)表繼承
  2. 連接繼承

第三項(每個(gè)具體的類(lèi)一個(gè)表)由 Hibernate 提供,但通常很少用,它是 JPA 持久性提供程序(如 OpenJPA)的可選實(shí)現。

a. 單個(gè)表繼承

對于 Java 基礎類(lèi)包含其所有子類(lèi)的大多數屬性的情況,可以使用單個(gè)表映射繼承,該表中的一列值標識特定的子類(lèi),行所表示的實(shí)例屬于此類(lèi)。如果沒(méi)有任何列映射到特定的子類(lèi),則這些列必須聲明為可以為空,因為它們在該子類(lèi)的數據庫行中將為空。

此繼承策略的缺點(diǎn)是如果子類(lèi)為該實(shí)例定義多個(gè)非空屬性,則非空約束的丟失會(huì )帶來(lái)數據完整性問(wèn)題。此方法的主要優(yōu)點(diǎn)是,它為類(lèi)層次范圍的實(shí)體和查詢(xún)之間的多態(tài)關(guān)系提供最佳支持,因為不存在復雜的連接。

  • 對象模型

    映射 1. 單個(gè)表繼承 (POJO)
                    // Participant (base) class                public class Participant implements Serializable{                private Long participantId;                ...                }                // SalesRep (sub) class                public class SalesRep extends Participant { ... }                // Administrator (sub) class                public class Administrator extends Participant { ... }

  • Hibernate 約定

    在 Hibernate 中,單個(gè)表繼承按照以下方式進(jìn)行映射:

    • 在基礎類(lèi)中,將類(lèi)與映射到表的辨別器列一起使用;還要為主鍵和其他屬性定義映射(稍后進(jìn)行介紹,不在示例中顯示)。
    • 將子類(lèi)與子類(lèi)中獨特的辨別器值一起使用;還要為子類(lèi)獨有的屬性定義映射。您不能在子類(lèi)中定義 ID 元素;它們沒(méi)有自已的表,因此使用(基礎)類(lèi)的 ID。


    映射 2. 單個(gè)表繼承(Hibernate XML 映射)
                    <!-- Participant (base) class -->                <class name="Participant" table="T_PRTCPNT" >                <id name="participantId" column="PRTCPNT_TID"/>                <discriminator column="PRTCPNT_TYPE" type="string"/>                ...                </class>                <!-- SalesRep subclass -->                <subclass                name="SalesRep"                extends="Participant"                discriminator-value="SALES_REP">                ...                </subclass>                <!-- Administrator subclass -->                <subclass name="Administrator"                extends="Participant"                discriminator-value="ADMIN">                ...                </subclass>

  • OpenJPA 約定

    在 OpenJPA 中,單個(gè)表繼承按照以下方式進(jìn)行映射:

    • 在基礎類(lèi)中使用 SINGLE_TABLE 繼承策略和辨別器列;還要定義基礎類(lèi)的持久屬性及其唯一的 ID。
    • 使用子類(lèi)中的辨別器值表示其實(shí)例;還要定義子類(lèi)的持久屬性,而不是 ID。子類(lèi)不會(huì )有任何表;它們的屬性將提升到表示基礎類(lèi)的表。


    映射 3. 單個(gè)表繼承(OpenJPA XML 映射)
                    <entity class="Participant">                <table name="T_PRTCPNT"/>                <inheritance strategy="SINGLE_TABLE"/>                <discriminator-column name="PRTCPNT_CLASS"/>                <attributes>                <id name="participantId">                <column name="PRTCPNT_TID"/>                </id>                ...                </attributes>                </entity>                // SalesRep subclass                <entity class="SalesRep">                <discriminator-value>SALES_REP</discriminator-value>                ...                </entity>                // Administrator subclass                <entity class="Administrator">                <discriminator-value>ADMIN</discriminator-value>                ...                </entity>

b. 連接繼承

對于基礎類(lèi)不包含所有子類(lèi)的大多數屬性的情況,對于每個(gè)子類(lèi),將一個(gè)包含基礎類(lèi)屬性的表與一個(gè)單獨的連接表一起使用。對于非繼承屬性,該表僅包含列。因此,閱讀子類(lèi)實(shí)例需要跨基礎類(lèi)表和子類(lèi)表進(jìn)行連接。

連接繼承策略的優(yōu)點(diǎn)是您可以在子類(lèi)中定義非空屬性,而對應的缺點(diǎn)是需要多個(gè)連接才能構造實(shí)例。此方法也是最靈活的的方法,即您可以定義新的子類(lèi),并將屬性添加到現有子類(lèi),而無(wú)需修改基礎類(lèi)表。

  • 對象模型

    映射 4. 連接繼承 (POJO)
                    // Participant (base) class                public class Participant implements Serializable {                private Long participantId;                ...                }                // SalesRep (sub) class                public class SalesRep extends Participant {                ...                }                // Administrator (sub) class                public class Administrator extends Participant {                ...                }

  • Hibernate 約定

    在 Hibernate 中,連接繼承按照以下方式進(jìn)行映射:

    • 在基礎類(lèi)中,將類(lèi)元素與主鍵 (id) 一起使用;還要為這些屬性定義映射,以建立基礎類(lèi)。
    • 在子類(lèi)中,將連接子類(lèi)與包含基礎類(lèi)主鍵的外鍵列一起使用;還要在連接子類(lèi)中定義其他屬性的映射(本例中不顯示)。


    映射 5. 連接繼承(Hibernate XML 映射)
                    <!-- Participant (base) class -->                <class name="Participant" table="T_PRTCPNT" >                <id name="participantId" column="PRTCPNT_TID"/>                ...                </class>                <!-- SalesRep joined-subclass -->                <joined-subclass                name="SalesRep"                extends="Participant"                table="T_SALESREP">                <key column="PRTCPNT_TID"/>                ...                </joined-subclass>                <!-- Administrator joined-subclass -->                <joined-subclass                name="Administrator"                extends="Participant"                table="T_ADMIN">                <key column="PRTCPNT_TID"/>                ...                </joined-subclass>

  • OpenJPA 約定

    在 OpenJPA 中,連接繼承按照以下方式進(jìn)行映射:

    • 在基礎類(lèi)中,使用 JOINED 繼承策略?;A類(lèi)還定義所有連接子類(lèi)使用的主鍵,并可以選擇定義版本列。還要定義基礎類(lèi)屬性的映射。
    • 在子類(lèi)中,定義子類(lèi)的持久屬性;其表將包含這些屬性,并包含用作外鍵的主鍵列,以連接基礎類(lèi)的主鍵。子類(lèi)不定義版本列。


    映射 6. 連接繼承(OpenJPA XML 映射)
                    <!-- Participant (base) class -->                <entity class="Participant">                <table name="T_PRTCPNT"/>                <inheritance strategy="JOINED"/>                <attributes>                <id name="participantId">                <column name="PRTCPNT_TID"/>                </id>                ...                </attributes                </entity>                <!-- SalesRep subclass -->                <entity class="SalesRep">                <table name="T_SALESREP"/>                <primary-key-join-column name="PRTCPNT_TID"/>                ...                </entity>                <!—Administrator subclass -->                <entity class="Administrator">                <table name="T_ADMIN"/>                <primary-key-join-column name="PRTCPNT_TID"/>                ...                </entity>

2. 關(guān)系

對象模型中的對象之間(和數據模型中的表之間)需要多種關(guān)系。當數據模型在類(lèi)似數據類(lèi)的任何列之間未指定關(guān)系時(shí),對象模型必須明確對象之間的關(guān)系以支持遍歷。此外,數據模型中的關(guān)系沒(méi)有任何固有方向(盡管按一個(gè)方向搜索可以比另一個(gè)方向更有效)。而對象模型關(guān)系固有包含從一個(gè)對象到另一個(gè)對象的方向。

對象模型關(guān)系是在數據模型中實(shí)現的,方法是通過(guò)一個(gè)表中的外鍵引用另一個(gè)表中的主鍵。具有外鍵的表稱(chēng)為子對象。其行表示對象,該對象的生命周期依賴(lài)于他們引用的對象。因此,其表將包含父對象的外鍵。由于子對象具有外鍵,所以它必須始終指向有效的父對象;它不能是孤立的,這就意味著(zhù)要刪除父對象,必須首先刪除其子對象或從父對象到子對象執行級聯(lián)刪除。

為了維護關(guān)系,子對象也稱(chēng)為關(guān)系的所有者,而父對象稱(chēng)為非所有者。“所有者”這一概念非常重要,因為盡管 Java 程序員必須在兩邊設置雙向關(guān)系,但是數據庫只需更新一個(gè)值來(lái)反映這些更改;在子對象(或所有者)中該更新針對外鍵。因此,在子對象中,對表示外鍵的屬性的更改會(huì )傳播到數據庫,而對父對象中反向屬性的更改不會(huì )傳播到數據庫。

關(guān)系映射分為四個(gè)類(lèi)別:

  1. 一對一
  2. 多對一
  3. 一對多
  4. 多對多

a. 一對一關(guān)系

一對一關(guān)系定義到另一個(gè)持久對象的引用,其中子對象的生命周期完全依賴(lài)于父對象的生命周期。

在對象模型中使用組件對象建模一對一關(guān)系仍將導致兩個(gè)獨立的類(lèi),但在數據模型中不會(huì )有兩個(gè)獨立的表。

一對一關(guān)系有異常情況。如果發(fā)生異常,Hibernate 中的常見(jiàn)做法是將其建模為組件對象,這樣子表中的所有屬性都將展開(kāi)到父表中,因此在訪(fǎng)問(wèn)子表的屬性時(shí),無(wú)需連接父表和子表。

還可以使用兩個(gè)其他方法在 Hibernate 中建模一對一關(guān)系:

  • 將多對一元素與表之間的外鍵關(guān)聯(lián)一起使用;請按照多對一關(guān)系指導原則進(jìn)行操作。
  • 將一對一元素與表之間的主鍵關(guān)聯(lián)一起使用;使用 OpenJPA 中的一對一元素進(jìn)行映射。

本部分的其余內容將介紹在 Hibernate 中使用組件元素遷移一對一關(guān)系,并介紹如何將其遷移到 OpenJPA 中的嵌入對象。

  • 對象模型


    映射 7. 一對一關(guān)系 (POJO)
                    // Employee (parent) class                public class Employee implements Serializable {                private Long employeeId;                private EmployeeRecord employeeRec;                ...                }                // EmployeeRecord (child) class                public class EmployeeRecord implements Serializable { ... }

  • Hibernate 約定

    在 Hibernate 中,使用組件對象的一對一關(guān)系按照以下方式進(jìn)行映射:

    • 使用類(lèi)建模父類(lèi);在父類(lèi)中,還要定義主鍵 (id) 和父類(lèi)的其他屬性。
    • 使用嵌套組件元素建模子類(lèi);還可以在嵌套組件中定義其他屬性(如果需要)。


    映射 8. 一對一關(guān)系和組件對象(Hibernate XML 映射)
                    <!—Employee (parent) class                <class name="Employee" table="T_EMPLOYEE">                ...                <id name="employeeId" column="EMP_TID"/>                <!-- EmployeeRecord (child) class                <component name="employeeRec" class="EmployeeRecord">                ...                </component>                </class>

  • OpenJPA 約定

    在 OpenJPA 中,使用可嵌入對象的一對一關(guān)系按照以下方式進(jìn)行映射:

    • 在父實(shí)體中聲明嵌入字段(例如 Employee)。將嵌入字段映射為父實(shí)體的數據庫記錄的一部分,并嵌入父實(shí)體,而不是形成與子實(shí)體的關(guān)系。
    • 將子實(shí)體(例如 EmployeeRecord)定義為可嵌入實(shí)體。


    映射 9. 一對一關(guān)系和可嵌入對象(OpenJPA XML 映射)
                    <!-- Employee (parent) class -->                <entity class="Employee">                <table name="T_EMPLOYEE"/>                <attributes>                <id name="employeeId">                <column name="EMP_TID"/>                </id>                <embedded name="employeeRec"/>                ...                </attributes>                </entity>                <!-- EmployeeRecord (child) class -->                <embeddable class="EmployeeRecord">                ...                </embeddable>

b. 多對一關(guān)系

多對一關(guān)系定義到單個(gè)持久對象的引用。盡管多對一關(guān)系可以是單向的,但是經(jīng)常將其定義為一對多雙向關(guān)系的反向過(guò)程。

聲明多對一關(guān)系的實(shí)體是子對象(或關(guān)系的所有者),因為其表包含外鍵,而聲明多對一關(guān)系的實(shí)體引用的對象是父對象。由于其表不包含外鍵,所以它是非所有者或關(guān)系的反向。

  • 對象模型


    映射 10. 多對一關(guān)系 (POJO)
                    // Address (parent) class                public class Address implements Serializable {                private Long addressId;                ...                }                // Phone (child) class                public class Phone implements Serializable {                private Address address;                ...                }

  • Hibernate 約定

    在 Hibernate 中,多對一關(guān)系按照以下方式進(jìn)行映射:

    • 在子類(lèi)中使用多對一元素。
    • 在父類(lèi)中定義主鍵。


    映射 11. 多對一關(guān)系(Hibernate XML 映射)
                    <!-- Phone (child) class -->                <class name="Phone" table="T_PHONE">                <many-to-one                name="address"                class="Address"                column="ADDR_TID">                </many-to-one>                ...                </class>                <!-- Address (parent) class -->                <class name="Address" table="T_ADDRESS">                <id name="addressId" column="ADDR_TID"/>                ...                </class>

  • OpenJPA 約定

    在 OpenJPA 中,多對一關(guān)系按照以下方式進(jìn)行映射:

    • 在父實(shí)體中定義主鍵 (id)。
    • 在子實(shí)體中使用多對一元素定義關(guān)系,并使用嵌套連接列元素定義外鍵。連接列指定如何按照連接的方法找到此子實(shí)體的父實(shí)體。


    映射 12. 多對一關(guān)系(OpenJPA XML 映射)
                    <!-- Address (parent) class -->                <entity class="Address">                <table name="T_ADDRESS"/>                <attributes>                <id name="addressId">                <column name="ADDR_TID"/>                </id>                ...                </attributes>                </entity>                <!-- Phone (child) class -->                <entity class="Child">                <table name="T_PHONE"/>                <attributes>                <many-to-one name="address">                <join-column name="ADDR_TID"/>                </many-to-one>                ...                </attributes>                </entity>

c. 一對多關(guān)系

一對多關(guān)系定義到對象集合的引用。由于用例通常需要從父對象到子對象的遍歷,而可能需要(也可能不需要)從子對象到父對象的遍歷,所以一對多關(guān)系是對象模型中最常見(jiàn)的關(guān)系類(lèi)型;這意味著(zhù)單向一對多關(guān)系可以滿(mǎn)足大多數情況。

也就是說(shuō),如果用例需要從子對象到父對象的遍歷,則可以在子類(lèi)中方便地添加多對一關(guān)系,使之成為雙向關(guān)系。

聲明一對多關(guān)系的實(shí)體是父對象(并且是非所有者)。此實(shí)體的表定義主鍵,但是它沒(méi)有外鍵(外鍵在子對象中)。

此一對多關(guān)系引用的對象是子對象和關(guān)系的所有者。子對象具有外鍵,并引用父對象的主鍵。

在 Hibernate 中,一對多關(guān)系的映射通常是通過(guò)將列添加到外鍵的子表完成的,但映射的詳細內容是不同的,具體取決于是單向一對多關(guān)系,還是雙向一對多關(guān)系。

在單向關(guān)系中,子表中的外鍵列不會(huì )映射到子對象中的屬性,它在數據模型中,而不是在對象模型中。由于是單向的,所以?xún)H在父對象中有屬性,而子對象中沒(méi)有。此外,必須將外鍵列定義為可以為空,因為 Hibernate 將首先插入子行(使用 NULL 外鍵),并在以后更新它。

在雙向關(guān)系中,對象關(guān)系映射比較好,因為子對象中有一個(gè)用于外鍵列的屬性,在數據庫中該列不必為空。但是,結果對象模型在對象之間有循環(huán)依賴(lài)關(guān)系和更緊密的耦合關(guān)系,并需要其他編程來(lái)設置關(guān)系的兩端。

可以看出,關(guān)于一對多關(guān)系的定義,有多個(gè)要考慮的權衡因素,但是通常建議使用單向關(guān)系,除非用例指示需要用兩個(gè)方向導航。

  • 對象模型


    映射 13. 一對多關(guān)系 (POJO)
                    // Address (parent) entity                public class Address implements Serializable {                private Long addressId;                private Set phones = new HashSet();                ...                }                // Phone (child) entity                public class Phone implements Serializable {                ...                }

  • Hibernate 約定

    在 Hibernate 中,一對多(單向)關(guān)系按照以下方式進(jìn)行映射:

    • 在父類(lèi)中,將設置、包或列表與一對多子元素一起使用。
    • 如果關(guān)系是單向的,則在表示子類(lèi)的表中創(chuàng )建外鍵;否則,使用雙向關(guān)系的多對一關(guān)系。


    映射 14. 一對多關(guān)系(Hibernate XML 映射)
                    <!-- Address (parent) class -->                <class name="Address" table="T_ADDRESS">                <id name="addressId" column="ADDR_TID"/>                <set                name="phones"                table="T_PHONE"                cascade="all-delete-orphan">                <key column="ADDR_TID"/>                <one-to-many class="Phone">                </set>                </class>                <!-- Phone (child) class -->                <class name="Phone" table="T_PHONE">                ...                </class>

    務(wù)必注意 Hibernate 中的專(zhuān)有 cascade="all-delete-orphan" 功能(請參見(jiàn)映射 14)。使用此屬性使您能夠從數據庫刪除子對象,方法是直接從父集合中移除它,然后將父對象保存到數據庫。使用此專(zhuān)有功能,不在代碼中顯式刪除子對象。盡管在標準 JPA 中沒(méi)有任何等效功能,但 OpenJPA 中的專(zhuān)有 @ElementDependent 注釋提供了自動(dòng)孤立項清除功能,但是此功能不可移植到其他持久性提供程序,并且在取讀代碼時(shí)可導致混淆。接下來(lái)將詳細討論 all-delete-orphan 功能。

  • OpenJPA 約定

    在 OpenJPA 中,您無(wú)法使用外鍵映射一對多單向關(guān)系,您必須使用連接表映射它。也就是說(shuō),如果關(guān)系是雙向的,則可以使用外鍵映射。這樣,有兩個(gè)選項可以從 Hibernate 映射一對多單向關(guān)系:

    1. 使用連接表映射并將連接表添加到數據庫,或
    2. 使用外鍵映射并將關(guān)系從單向轉換為雙向,將反向屬性添加到對象模型,并更改代碼,以便將關(guān)系設置為兩個(gè)方向。

    建議將單向關(guān)系轉換為雙向關(guān)系,因為與改變現有數據庫模式和關(guān)聯(lián)的行相比,此方法通常更容易修改遺留代碼。

    在 OpenJPA 中,一對多(雙向)關(guān)系按照以下方式進(jìn)行映射:

    • 在父對象中,使用一對多元素定義子對象的集合,并使用 id 元素為父對象定義主鍵。
    • 在子類(lèi)中,使用多對一元素定義父對象,并使用嵌套連接列元素定義子對象中的外鍵,并指定如何連接父對象和子對象。


    映射 15 一對多關(guān)系(OpenJPA XML 映射)
                    <!-- Address (parent) class -->                <entity class="Address">                <table name="T_ADDRESS"/>                <attributes>                <id name="addressId">                <column name="ADDR_TID"/>                </id>                <one-to-many name="phones" mapped-by="address">                <cascade>                <cascade-all/>                </cascade>                </one-to-many>                ...                </attributes>                </entity>                <!-- Phone (child) class -->                <entity class="Phone">                <table name="T_PHONE"/>                <attributes>                <many-to-one name="address">                <cascade>                <cascade-all/>                </cascade>                <join-column name="ADDR_TID"/>                </many-to-one>                ...                </attributes>                </entity>

在定義一對多關(guān)系時(shí),通常使用一些其他功能,您需要知道如何遷移這些功能:

  • Hibernate

    • inverse="true"
      在 Hibernate 中,您可能會(huì )遇到在定義雙向關(guān)系時(shí)使用的 inverse="true" 屬性。如果是這樣,請不要擔心,因為此功能等效于 OpenJPA 指定的關(guān)系的非所有者(它的表沒(méi)有外鍵)。類(lèi)似地,Hibernate inverse="false" 屬性等效于 OpenJPA 的關(guān)系的所有者(它的表有外鍵)。

      通常,這些概念是一致的,但使用 inverse="true" 設置在雙向關(guān)系的多對一端定義 Hibernate 映射這一情況除外。如果發(fā)現此類(lèi)映射,則應在執行遷移之前更改它,因為它在 Hibernate 中不是最佳實(shí)踐,并且不能生成最佳的 SQL。例如,如果將多對一端設置為 inverse="true",則每次創(chuàng )建子對象時(shí),Hibernate 將執行兩個(gè) SQL 語(yǔ)句,一個(gè)創(chuàng )建子對象,一個(gè)使用父對象的外鍵更新子對象。在多對一端將其更改為 inverse="false" 并在一對多端設置 inverse="true" 可以解決這一問(wèn)題,并使它與 OpenJPA 保持一致。當然,進(jìn)行該更改后,您應重新測試應用程序。

    • cascade="all-delete-orphan"
      JPA 規范中沒(méi)有與此 Hibernate 功能等效的功能,但是 OpenJPA 持久性提供程序中的專(zhuān)有 @ElementDependent 注釋完全可以執行 Hibernate 的 all-delete-orphan 功能執行的任務(wù)。如果需要使用該功能,請參閱 OpenJPA 用戶(hù)指南。盡管它是專(zhuān)有功能,但是,如果使用它遷移 all-delete-orphan 特性,則不需要對應用程序源代碼進(jìn)行任何更改。

      若要以符合標準的方式遷移 all-delete-orphan 功能,您可以使用 OneToMany 注釋中的 cascade=CascadeType.ALL 屬性,并更改源代碼,不僅從父集合中移除子對象,而且從數據庫中顯式刪除子對象;例如,不需要類(lèi)似于以下的代碼:

      public class Address implements Serializable {                    ...                    public void removePhone(Phone p) {                    this.phones.remove(p); // Explicitly remove p from the set; which                    // Implicitly deletes p from the database                    }                    }

      您應使用類(lèi)似于以下的代碼替換它:

      public class Address implements Serializable {                    ...                    public void removePhone(Phone p) {                    // Explicitly remove p from the set                    this.phones.remove(p);                    // Explicitly delete p from the database                    ORMHelper.getCurrentSession().delete(p);                    }                    }

  • OpenJPA

    • cascade=CascadeType.ALL
      盡管上面的 Hibernate 示例僅指定了從父對象到子對象的級聯(lián)操作,但是在使用 OpenJPA 持久性提供程序測試此映射時(shí),我們必須將 cascade=CascadeType.ALL 定義為兩個(gè)方向,并在 OneToMany 注釋和 ManyToOne 注釋中指明。通常,您不需要從子對象到其父對象的級聯(lián)操作(在 ManyToOne 注釋中),但是這對于使從父對象到子對象的 merge() 級聯(lián)操作能夠工作是必需的。

      如果不按兩個(gè)方向定義級聯(lián)操作,則 remove() 和 persist() 的級聯(lián)操作將從父 Address 對象到其依賴(lài)的 Phone 對象進(jìn)行,但是 merge() 的級聯(lián)操作會(huì )引發(fā)異常,指示依賴(lài)對象中的 Phone.address 字段不允許級聯(lián)。

      (這是當前正在使用的 OpenJPA v0.9.7 中的已知問(wèn)題,在以后的版本中會(huì )修復該問(wèn)題。與此同時(shí),解決辦法是按兩個(gè)方向啟用級聯(lián)。)

d. 多對多關(guān)系

多對多關(guān)系通過(guò)映射表定義到對象集合的引用。在對象模型中,多對多關(guān)系并不是全部通用的,但是它們通常是雙向的。

與其他雙向關(guān)系一樣,存在所屬端和非所屬端。在這種情況下,所屬端擁有映射表,而不是外鍵??梢詫⑷我庖欢酥付樗鶎俣?;選取哪一端并不重要。

  • 對象模型


    映射 16. 多對多關(guān)系 (POJO)
                    // Group (non-owner/parent) entity                public class Group implements Serializable {                private Long groupId;                private Set users = new HashSet();                ...                }                // User (owner/child) entity                public class User implements Serializable {                private Long userId;                private Set groups = new HashSet();                ...                }

  • Hibernate 約定

    在 Hibernate 中,多對多關(guān)系按照以下方式進(jìn)行映射:

    • 非所有者將集合(設置、包或列表)元素與 inverse="true" 屬性、表屬性、鍵子元素和多對多子元素一起使用。
    • 關(guān)系所有者將集合(設置、包或列表)元素與表屬性、鍵子元素和多對多子元素一起使用。


    映射 17. 多對多關(guān)系(Hibernate XML 映射)
                    <!—Group (non-owner/parent) class -->                <class name="Group" table="T_GROUP">                <id name="groupId" column="GROUP_TID"/>                <set name="users" table="T_USER_GROUP" inverse="true">                <key column="GROUP_TID"/>                <many-to-many column="USER_TID" class="User"/>                </set>                </class>                <!—User (owner/child) class -->                <class name="User" table="T_USER">                <id name="userId" column="USER_TID"/>                <set name="groups" table="T_USER_GROUP">                <key column="USER_TID"/>                <many-to-many column="GROUP_TID" class="Group"/>                </set>                </class>

  • OpenJPA 約定

    在 OpenJPA 中,多對多關(guān)系按照以下方式進(jìn)行映射:

    • 在所有者/子對象中,使用多對多元素和嵌套連接表元素指定如何創(chuàng )建關(guān)系。您還將 id 與嵌套列元素一起使用,以指定連接表引用的主鍵名稱(chēng)。
    • 在非所有者/父對象中,將多對多元素與 mapped-by 屬性一起使用,以引用聲明連接表的類(lèi)中的屬性。(連接表包含創(chuàng )建關(guān)系的所有信息。您還需要通過(guò) id 元素創(chuàng )建主鍵。)


    映射 18. 多對多關(guān)系(OpenJPA XML 映射)
                    <!-- User (owner/child) class -->                <entity class="User">                <table name="T_USER"/>                <attributes>                <id name="userId">                <column name="USER_TID"/>                </id>                <many-to-many name="groups">                <join-table name="T_USER_GROUP">                <join-column name="USER_TID"/>                <inverse-join-column name="GROUP_TID"/>                </join-table>                </many-to-many>                ...                </attributes>                </entity>                <!-- Group (non-owner/parent) class -->                <entity class="Group">                <table name="T_GROUP"/>                <attributes>                <id name="groupId">                <column name="GROUP_TID"/>                </id>                <many-to-many name="users" mapped-by="groups"/>                ...                </attributes>                </entity>

3. 延遲初始化

可以將延遲初始化應用到任何字段,但是它經(jīng)常與一對多或多對多關(guān)系一起使用,在讀取父對象時(shí),您還可以控制是否需要數據庫返回所有子對象。

為進(jìn)一步闡述此概念,請考慮使用從 A 到 B 的一對多或多對多關(guān)系。如果在該關(guān)系中將 fetch 設置為 LAZY,則從數據庫讀取 A 將不能從該數據庫讀取 B,直到代碼嘗試遍歷從 A 到 B 的關(guān)系。另一方面,如果將 fetch 設置為 EAGER,則從數據庫讀取 A 也可以從該數據庫讀取依賴(lài)的 B 對象。

在使用 LAZY 或 EAGER 的 fetch 定義關(guān)系時(shí),請務(wù)必小心,尤其是在按照一般模式,EJB 層將分離實(shí)體返回到 Web 層并且 Web 層訪(fǎng)問(wèn)該數據以呈現視圖時(shí)更要小心。使用 LAZY 加載,Web 層可以沒(méi)有呈現視圖必需的依賴(lài)對象。另一方面,您不能使用 EAGER 直接加載所有關(guān)系,因為每次返回的信息比必要信息多。

原因在于您必須使用滿(mǎn)足業(yè)務(wù)需求的適當獲取策略定制域模型。如果用例需要父對象和子對象,則使用 EAGER 加載。如果用例不需要子對象,則使用 LAZY 加載。

  • 對象模型


    映射 19. 延遲初始化 (POJO)
                    // Address (parent) entity                public class Address implements Serializable {                private Long addressId;                private Set phones = new HashSet();                ...                }                // Phone (child) entity                public class Phone implements Serializable {                private Address address;                ...                }

  • Hibernate 約定

    在 Hibernate 中,缺省值是用于集合的延遲初始化,它實(shí)現一對多或多對多關(guān)系。要禁用延遲(或啟用 eager)初始化,請按照以下方式進(jìn)行映射:

    • 在父對象中,將集合(設置、包或列表)與 lazy=false 屬性一起使用。
    • 在子對象中,將類(lèi)與 lazy=false 屬性一起使用以啟用獲取。


    映射 20. 延遲初始化(Hibernate XML 映射)
                    <!-- Address (parent) class -->                <class name="Address" table="T_ADDRESS">                ...                <id name="addressId" column="ADDR_TID"/>                <set                name="phones"                table="T_PHONE"                cascade="all-delete-orphan"                inverse="true"                lazy="false">                <key column="ADDR_TID"/>                <one-to-many class="Phone"/>                </set>                </class>                <!-- Phone (child) class -->                <class name="Phone" table="T_PHONE" lazy="false">                ...                <many-to-one                name="address"                class="Address"                column="ADDR_TID">                </many-to-one>                </class>

  • OpenJPA 約定

    在 OpenJPA 中,一對多和多對一關(guān)系的延遲初始化也是缺省值。要禁用延遲初始化(和啟用 eager 初始化),請按照以下方式進(jìn)行映射:

    • 在父實(shí)體的集合中使用 fetch=FetchType.EAGER 屬性。


    映射 21. 延遲初始化(OpenJPA XML 映射)
                    <!-- Address (parent) class -->                <entity class="Address">                <table name="T_ADDRESS"/>                <attributes>                <id name="addressId">                <column name="ADDR_TID"/>                </id>                <one-to-many name="phones" mapped-by="address" fetch="EAGER">                <cascade>                <cascade-all/>                </cascade>                </one-to-many>                ...                </attributes>                </entity>                <!-- Phone (child) class -->                <entity class="Phone">                <table name="T_PHONE"/>                <attributes>                <many-to-one name="address">                <cascade>                <cascade-all/>                </cascade>                <join-column name="ADDR_TID"/>                </many-to-one>                ...                </attributes>                </entity>

還值得一提的是,Hibernate 和 OpenJPA 在從分離對象訪(fǎng)問(wèn)延遲加載的集合上是不同的。在 Hibernate 中,如果程序員嘗試訪(fǎng)問(wèn)分離對象上延遲加載的集合,則會(huì )引發(fā)異常;而 OpenJPA 將返回空值,而不是異常。

此差異的原因是 JPA 規范沒(méi)有指定如何處理在分離對象上訪(fǎng)問(wèn)延遲加載的集合。每個(gè) JPA 供應商可以決定如何處理此條件。它們會(huì )引發(fā)異常,或者將其保留為未初始化狀態(tài),甚至返回具有零個(gè)元素的集合。

因此,如果遺留 Hibernate 應用程序正在使用異常檢測對分離對象的延遲加載集合的訪(fǎng)問(wèn),您可以使用 OpenJPA 通過(guò)測試空集合執行相同的操作。不過(guò),需要記住的是:JPA 規范沒(méi)有說(shuō)明是引發(fā)異常還是返回空值,因此依賴(lài)于此行為不可移植,并且隨時(shí)會(huì )更改,甚至可能在以后版本中中斷您的應用程序。

此外,還要務(wù)必注意您是獲取異常(在 Hibernate 中獲?。?,還是不獲取異常(在 OpenJPA 中不獲?。?,異??梢灾甘驹跍y試過(guò)程中需要檢測和修復的應用程序錯誤。也就是說(shuō),問(wèn)題在于您是否獲取異常,所以在 JPA 規范中強制引發(fā)異常與否不能解決問(wèn)題:為了使用分離對象,應用程序需要某些體系結構指導原則。

有以下三個(gè)解決方案使用分離對象:

  1. 在返回視圖所需的所有集合之前,請顯式初始化它們;即您應確定延遲加載是否適用于這些特定的關(guān)系。
  2. 保持實(shí)體管理器處于打開(kāi)狀態(tài),直到完成呈現視圖(即在視圖中打開(kāi)和關(guān)閉實(shí)體管理器),這可以全部避免使用分離實(shí)體。
  3. 與第二個(gè)解決方案類(lèi)似,EJB 3.0 的第三個(gè)可能的解決方案使用擴展的持久上下文,以保持實(shí)體管理器在事務(wù)之間處于活動(dòng)狀態(tài)。

如果將實(shí)體從 EJB 組件傳遞到 Web 層,則應遵循第一個(gè)解決方案,因為它使您能夠保持 EJB(會(huì )話(huà) facade)中的事務(wù)邏輯,而不是需要 Web 層管理該事務(wù)。此外,Web 層難于管理事務(wù),因為它需要提交事務(wù),并在呈現視圖后關(guān)閉實(shí)體管理器(可能在 ServletFilter 中)。

關(guān)于分離對象的最后一點(diǎn)是您應該讓對象模型符合業(yè)務(wù)用例。如果某些用例需要集合的延遲加載,而其他用例需要該集合的 eager 加載,則在對象模型中使用集合的延遲加載,并根據需要在會(huì )話(huà) facade 中強制執行 eager 加載。即,在會(huì )話(huà) facade 中為不同的用例定義不同的方法:一種方法僅返回分離對象,而另一種方法加載子對象,然后返回分離的對象。共有三種方法可加載 OpenJPA 中的子對象;前兩種方法為 JPA 標準,最后一種方法為 OpenJPA 擴展:

  1. 通過(guò)調用集合上的 size(),進(jìn)行集合的 Trigger 加載。
  2. 將 JP-QL 與 Fetch Join 功能一起使用,臨時(shí)覆蓋 Lazy fetch 類(lèi)型。
  3. 使用 OpenJPA FetchGroups 注釋加載子對象。

4. 對象標識

數據模型中的所有表與表的主鍵一樣包括稱(chēng)為 OID 的對象標識列。OID 的最佳實(shí)踐是使用其值由系統分配的整數列。在這種情況下,OID 使用 java.lang.Long 映射到對象模型的主鍵。同時(shí),表(外鍵)之間的所有關(guān)系也都基于關(guān)系表的 OID 列。

在使用通常的 Hibernate Sequence 生成器類(lèi)插入任何新行時(shí),Hibernate 會(huì )分配 ID,它使用基礎數據庫支持序列,其中每個(gè)表都有一行包含表名稱(chēng)和當前 OID 號。在此方法中,OID 對每個(gè)表都是唯一的(OID 在表之間可能不是唯一的)。

使用 OID 為沒(méi)有業(yè)務(wù)含義的所有數據提供鍵。這些系統生成的鍵沒(méi)有任何業(yè)務(wù)含義,被稱(chēng)為人工鍵,而不是具有業(yè)務(wù)含義的自然鍵。使用人工鍵可以更改數據庫中任何具有業(yè)務(wù)含義的數據,而不用擔心違反約束。

在現有 Hibernate 模型中,您通常還會(huì )發(fā)現大多數表具有可以包括許多列的具有業(yè)務(wù)含義的標識。您可能還會(huì )發(fā)現在數據模型中的任何此類(lèi)候選主鍵上定義了唯一約束。此數據庫約束可確保唯一性,并且還基于業(yè)務(wù)含義為查找此類(lèi)實(shí)體提供索引。

您可能會(huì )遇到某些特殊情況,其中數據必須預填充到某些表,以表示沒(méi)有任何業(yè)務(wù)含義的數據行(對于代碼表和類(lèi)似內容)。為此,在啟動(dòng)應用程序之前,必須將這些行插入到數據庫,這些行的 OID 必須小于用于該表的 SEQUENCE 表的起始值。在這種情況下,您可能會(huì )發(fā)現初始值大于 1 的序列表。

  • 對象模型


    映射 22. 對象標識 (POJO)
                    / Address entity                public class Address implements Serializable {                private Long addressId;                ...                }

  • Hibernate 約定

    在 Hibernate 中,對象標識按照以下方式進(jìn)行映射:

    • 將 id 與嵌套生成器子元素一起使用。


    映射 23. 對象標識(Hibernate XML 映射)
                    <class name="Address">                ...                <id name="addressId" column="ADDR_TID">                <generator class="sequence">                <param name="sequence">T_ADDRESS_SEQ</param>                </generator>                </id>                ...                </class>

  • OpenJPA 約定

    在 OpenJPA 中,同一對象標識按照以下方式進(jìn)行映射:

    • 使用 Id、SequenceGenerator 和 GeneratedValue 注釋。


    映射 24. 對象標識(OpenJPA XML 映射)
                    /<!-- Address entity -->                <entity class="Address">                <sequence-generator name="AddressSeq" sequence-name="T_ADDRESS_SEQ"/>                <attributes>                <id name="addressId">                <column name="ADDR_TID"/>                <generated-value strategy="SEQUENCE" generator="AddressSeq"/>                </id>                ...                </attributes>

對于 Hibernate 和 OpenJPA,OID 的現有序列表的 DDL 為:

create sequence T_ADDRESS_SEQ;

關(guān)于對象標識符的最后一點(diǎn)說(shuō)明:數據模型中的所有外鍵通?;陉P(guān)系表的 OID 主鍵。所有外鍵在引用主鍵的數據模型中都具有約束,并定義了 ON DELETE RESTRICT 語(yǔ)義,這樣,如果仍然存在該父對象的子對象的行,則數據庫可以防止您從該數據庫刪除行(例如,您無(wú)法刪除父對象)。

5. 樂(lè )觀(guān)鎖定

Hibernate 為并發(fā)控制提供樂(lè )觀(guān)和悲觀(guān)鎖定模式。為獲得短時(shí)間的事務(wù)和較好的性能,大多數遺留 Hibernate 應用程序使用樂(lè )觀(guān)鎖定。當用戶(hù)編輯屏幕上的數據時(shí),短時(shí)間的事務(wù)會(huì )釋放鎖定。樂(lè )觀(guān)鎖定會(huì )檢查對象的時(shí)間戳或版本,以確保當用戶(hù)編輯數據時(shí),沒(méi)有更改該對象。如果更改,則引發(fā)樂(lè )觀(guān)鎖定異常,通知用戶(hù)已發(fā)生異常,并且用戶(hù)可以刷新導致異常的數據,并重試事務(wù),以便下一次正常運行。通知用戶(hù)樂(lè )觀(guān)鎖定異常通常通過(guò)應用程序的常規異常處理發(fā)生。

  • 對象模型

    并發(fā)性會(huì )導致問(wèn)題,所有實(shí)體將包含版本屬性,持久性提供程序使用此屬性檢測同一記錄的并發(fā)修改。遺留應用程序只是將版本屬性視為不可改變,并且在從數據庫讀取實(shí)體時(shí),負責將該版本屬性保存在實(shí)體中,直到將實(shí)體寫(xiě)回數據庫。



    映射 25. 對象標識 (POJO)
                    // Address entity                public class Address implements Serializable {                private Long version;                ...                }

  • Hibernate 約定

    在 Hibernate 中,樂(lè )觀(guān)鎖定按照以下方式進(jìn)行映射:

    • 使用版本元素定義版本屬性。


    映射 26. 樂(lè )觀(guān)鎖定(Hibernate XML 映射)
                    <!-- Address class -->                <class name="Address" table="T_ADDRESS">                ...                <version name="version" column="VERSION"/>                ...                </class>

  • OpenJPA 約定

    在 OpenJPA 中,樂(lè )觀(guān)鎖定按照以下方式進(jìn)行映射:

    • 使用版本元素定義版本屬性。


    映射 27. 樂(lè )觀(guān)鎖定(OpenJPA XML 映射)
                    <!-- Address class -->                <entity class="Address">                <table name="T_PHONE"/>                <attributes>                ...                <version name="version">                <column name="VERSION"/>                </version>                ...                </attributes>                </class>

    Hibernate 支持樂(lè )觀(guān)和悲觀(guān)鎖定,但是 JPA 規范僅定義樂(lè )觀(guān)鎖定的概念。不過(guò),OpenJPA 支持樂(lè )觀(guān)鎖定,并為悲觀(guān)鎖定提供擴展。因此,如果遷移使用悲觀(guān)鎖定的遺留 Hibernate 應用程序,請考慮在 persistence.xml 文件中設置以下 OpenJPA 配置屬性:



    映射 28. 悲觀(guān)鎖定(OpenJPA 配置屬性)
                    <property                name="openjpa.LockManager"                value="pessimistic">                </property>                <property                name="openjpa.ReadLockLevel"                value="read">                </property>                <property                name="openjpa.WriteLockLevel"                value="write">                </property>                <property                name="openjpa.jdbc.TransactionIsolation"                value="repeatable-read">                </property>

    使用這些屬性,所有讀取內容都獲取共享(讀?。╂i定并保存它們,直到事務(wù)結束。如果在同一記錄具有多個(gè)并發(fā)更新版本時(shí)遇到并發(fā)性問(wèn)題(死鎖),則可能需要指定寫(xiě)入的 ReadLockLevel,以便在檢索數據時(shí)生成 FOR UPDATE,并強制執行更新版本的序列。

    如果使用配置參數在 persistence.xml 文件中指定這些悲觀(guān)鎖定級別,則它們將會(huì )應用于所有事務(wù)。另外,您可能需要使用 org.apache.openjpa.persistence.FetchPlan 類(lèi)以編程方式為單個(gè)事務(wù)設置鎖定級別,如以下代碼片段所示:



    映射 28. 悲觀(guān)鎖定(以編程方式設置的 OpenJPA 配置屬性)
                    import org.apache.openjpa.persistence.*;                ...                EntityManager em = ...;                em.getTransaction ().begin ();                ...                // load an object with a pessimistic read lock mode                fetch = ( OpenJPAPersistence.cast( em ) ).getFetchPlan();                fetch.setReadLockMode( LockModeType.WRITE );                Address address = em.find( Address.class, addressId );                ...                em.getTransaction ().commit ();





回頁(yè)首


遷移 Hibernate 配置參數

在 Hibernate 中,配置 SessionFactory 的最常見(jiàn)方法是在 hibernate.cfg.xml 文件中包括 <property> 元素,并將該文件放置在類(lèi)路徑的根文件夾中。另一個(gè)很少使用的等效方法是在類(lèi)路徑的 hibernate.properties 文件中包括屬性。

在 OpenJPA 中,EntityManagerFactory 是通過(guò)以下方法配置的:在 persistence.xml 文件中包括命名的 <persistence-unit> 元素,并將該文件放置在類(lèi)路徑的 META-INF 文件夾中。<persistence-unit> 定義持久性提供程序、映射文件和其他屬性,如數據庫連接和日志記錄。持久性提供程序標識實(shí)現 JPA 規范的供應商,<persistence-unit> 中的命名屬性將特定于持久性提供程序(本示例中為 OpenJPA)。

將 Hibernate 應用程序遷移到 OpenJPA 時(shí),至少會(huì )遇到三個(gè)常見(jiàn)配置場(chǎng)景:

  1. 數據庫連接——該配置屬性告訴 SessionFactory 如何連接到數據庫。
  2. 映射位置——該屬性控制對象到數據庫中行的映射。
  3. 日志類(lèi)別——該屬性使您能夠診斷問(wèn)題,如設置日志記錄/跟蹤級別。

您可能會(huì )遇到許多配置屬性,這里無(wú)法一一介紹,所以請一定要參閱參考資料,獲得關(guān)于映射其他配置屬性的信息。其中特別重要的參考資料是用于 org.hibernate.cfg.Environment 類(lèi)和所有 Hibernate 配置屬性的 Hibernate API 文檔OpenJPA 用戶(hù)指南。

1. 數據庫連接

有以下兩種方法配置數據庫連接:使用本地 JDBC 連接(這里將介紹它)或使用 J2EE 數據源(請參見(jiàn)參考資料)。

  • Hibernate 約定

    在 Hibernate 中,配置 JDBC 連接按照以下方式進(jìn)行映射:

    • 使用 dialect 配置參數。
    • 使用 connection.driver_class 配置參數。
    • 使用 connection.url 配置參數。
    • 使用 connection.username 配置參數。
    • 使用 connection.password 配置參數。


    配置 1. Hibernate 數據庫連接
                    ...                <hibernate-configuration>                <session-factory>                <property                name="dialect">                org.hibernate.dialect.DB2Dialect                </property>                <property                name="connection.driver_class">                com.ibm.db2.jcc.DB2Driver                </property>                <property                name="connection.url">                jdbc:db2://localhost:50000/HIBTEST                </property>                <property                name="connection.username">                db2admin                </property>                <property                name="connection.password">                db2admin                </property>                </session-factory>                </hibernate-configuration>

  • OpenJPA 約定

    在 OpenJPA 中,配置等效 JDBC 連接按照以下方式進(jìn)行映射:

    • 使用 openjpa.jdbc.DBDictionary 配置參數。(此參數是可選的,因為 OpenJPA 通??梢酝ㄟ^(guò) URL 和 DriverName 屬性確定正確的字典。)
    • 使用 openjpa.ConnectionDriverName 配置參數。
    • 使用 openjpa.ConnectionURL 配置參數。
    • 使用 openjpa.ConnectionUserName 配置參數。
    • 使用 openjpa.ConnectionPassword 配置參數。


    配置 2. OpenJPA 數據庫連接
                    <persistence ...>                <persistence-unit name="JPATEST">                <properties>                <property                name="openjpa.jdbc.DBDictionary"                value="db2(DriverVendor=db2)" >                </property>                <property                name="openjpa.ConnectionDriverName"                value="com.ibm.db2.jcc.DB2Driver">                </property>                <property                name="openjpa.ConnectionURL"                value="jdbc:db2://localhost:50000/JPATEST">                </property>                <property                name="openjpa.ConnectionUserName"                value="db2admin">                </property>                <property                name="openjpa.ConnectionPassword"                value="db2admin">                </property>                </properties>                </persistence-unit>                </persistence>

    (盡管此示例不需要另一個(gè)參數 openjpa.ConnectionProperties,但它非常有用,在執行 connect() 調用時(shí),它允許傳入其他屬性。)

2. 映射位置

如果您在 Hibernate 中使用基于 XML 的配置方法,則不僅可以指定配置參數,而且可以指定映射文件的位置。這是常見(jiàn)的場(chǎng)景,因為它使您能夠配置 SessionFactory,而無(wú)需以編程方式指定映射文件的位置。

  • Hibernate 約定

    在 Hibernate 中,配置映射文件的位置按照以下方式進(jìn)行映射:

    • 將 <mapping> 與資源屬性一起使用。


    配置 3. Hibernate 映射位置
                    ...                <hibernate-configuration>                <session-factory>                ...                <!-- Mapping files -->                <mapping resource="domainmodel.hbm.xml"/>                </session-factory>                </hibernate-configuration>

  • OpenJPA 約定

    在 OpenJPA 中,配置映射文件的位置按照以下方式進(jìn)行映射:

    • 使用 <mapping-file> 元素。


    配置 4. OpenJPA 映射位置
                    <persistence ...>                <persistence-unit name="TEST">                ...                <!-- Mapping files -->                <mapping-file>META-INF/orm.xml</mapping-file>                </persistence-unit>

3. 日志類(lèi)別

Hibernate 應用程序的故障排除非常困難,因為 Hibernate 會(huì )基于映射文件中指定的元數據為您生成 SQL 命令。因此,將日志類(lèi)別配置為查看 Hibernate 提交到數據庫的準確 SQL 通常非常有用。

  • Hibernate 約定

    在 Hibernate 中,日志類(lèi)別的配置按照以下方式進(jìn)行映射:

    • 使用 show_sql 配置參數輸出 SQL 命令。


    配置 5. Hibernate 日志類(lèi)別
                    ...                <hibernate-configuration>                <session-factory>                ...                <property                name="show_sql">                true                </property>                </session-factory>                </hibernate-configuration>

    如果研究 SQL 不足以診斷問(wèn)題,Hibernate 中還提供了其他日志類(lèi)別,但是您將發(fā)現在類(lèi)路徑的 log4j.properties 文件中配置了日志類(lèi)別。(此處不介紹該文件的配置;請參考 Hibernate 文檔,獲得關(guān)于配置其他 Hibernate 日志類(lèi)別的信息。)

  • OpenJPA 約定

    在 OpenJPA 中,日志記錄的配置按照以下方式進(jìn)行映射:

    • 使用 openjpa.Log 配置屬性輸出 SQL。
    • 使用 openjpa.ConnectionFactoryProperties 準確地輸出 SQL。


    配置 6. OpenJPA 日志類(lèi)別
                    <persistence ...>                <persistence-unit name="TEST">                ...                <properties>                <property                name="openjpa.Log"                value="SQL=TRACE">                </property>                <property                name="openjpa.ConnectionFactoryProperties"                value="PrettyPrint=true, PrettyPrintLineLength=72">                </property>                </properties>                </persistence-unit>

    您還可以使用 openjpa.Log 配置屬性將 OpenJPA 配置為輸出其他日志信息。(請參見(jiàn) OpenJPA 用戶(hù)指南。)





回頁(yè)首


結束語(yǔ)

本文詳細介紹了將使用 EJB 2.1 的專(zhuān)有 Hibernate 3 應用程序遷移到使用 OpenJPA 0.9.7 持久性提供程序和 EJB 3.0 的行業(yè)標準 JPA 應用程序的案例研究。通過(guò)一系列常見(jiàn)場(chǎng)景描述了遷移情況,并且在每個(gè)場(chǎng)景中,對 Hibernate 和 OpenJPA 的實(shí)現進(jìn)行了并排比較。并排比較為那些將現有 Hibernate/EJB 2.1 應用程序遷移到 OpenJPA/EJB 3.0,以及在下一個(gè)項目中使用 OpenJPA/EJB 3.0 的用戶(hù)提供了幫助。

本文介紹了如何遷移組成遺留 Hibernate 應用程序的三個(gè)主要構建塊(應用程序源代碼、對象關(guān)系映射和配置參數),并得出以下結論:

  • 如果 Hibernate 應用程序源代碼構造良好,并封裝 Hibernate 調用,則對于常見(jiàn)場(chǎng)景,源代碼的遷移非常簡(jiǎn)單。對于沒(méi)有封裝 Hibernate 調用的程序,遷移將比較困難,但是與更改業(yè)務(wù)(會(huì )話(huà)、事務(wù)和實(shí)體管理)邏輯相比,遷移還需要較多的語(yǔ)法更改。

  • 由于對象關(guān)系映射已存在,所以您需要使用中間相遇遷移方法保持現有對象和數據模型。不能使用自底向上方法從數據模型生成對象模型,也不能使用自頂向下方法從對象模型生成數據模型;遷移必須保持兩個(gè)模型。本文手動(dòng)實(shí)現了中間相遇映射,但是 Dali JPA 工具或 IBM Design Pattern 工具包(請參見(jiàn)參考資料)可以自動(dòng)進(jìn)行 Hibernate XML 到 OpenJPA XML的大部分遷移。

  • 將常見(jiàn)配置參數從 Hibernate 遷移到 OpenJPA 非常容易,但是還可以使用許多其他參數來(lái)優(yōu)化 OpenJPA 可能需要(也可能不需要)的遺留 Hibernate 應用程序。因此,對優(yōu)化不執行并排遷移。相反,先遷移常見(jiàn)配置參數,然后讓負載測試指示需要引入哪些 OpenJPA 優(yōu)化參數。

本文沒(méi)有介紹 Hibernate 查詢(xún)到 OpenJPA 的遷移,因為對于 Hibernate 應用程序來(lái)說(shuō),具有滿(mǎn)足業(yè)務(wù)需求的定義良好的域模型是比較普遍的。對于沒(méi)有定義良好的域模型和需要許多特殊查詢(xún)的應用程序,則解決方案(如 iBatis)比對象關(guān)系映射工具(如 Hibernate 或 OpenJPA)更合適。也就是說(shuō),如果有足夠的興趣,則后續文章可能將介紹 Hibernate 查詢(xún)到 OpenJPA 的遷移。





回頁(yè)首


附錄:OpenJPA Java 5 注釋


注釋 A. 單個(gè)表繼承

            //Participant (base) class            @Entity            @Table(name="T_PRTCPNT")            @Inheritance(strategy=InheritanceType.SINGLE_TABLE)            @DiscriminatorColumn(name="PRTCPNT_CLASS")            public class Participant implements java.io.Serializable {            @Column(name="PRTCPNT_TID")            @Id private Long participantId;            ...            }            // SalesRep subclass            @Entity            @DiscriminatorValue(value="SALES_REP")            public class SalesRep extends Participant {            ...            }            // Administrator subclass            @Entity            @DiscriminatorValue(value="ADMIN")            public class Administrator extends Participant {            ...            }


注釋 B. 連接繼承
            // Participant (base) class            @Entity            @Table(name="T_PRTCPNT")            @Inheritance(strategy=InheritanceType.JOINED)            public class Participant implements Serializable {            @Column(name="PRTCPNT_TID")            @Id private Long participantId;            ...            }            // SalesRep subclass            @Entity            @Table(name="T_SALESREP")            @PrimaryKeyJoinColumn(name="PRTCPNT_TID")            public SalesRep extends Participant {            ...            }            // Administrator subclass            @Entity            @Table(name="T_ADMIN")            @PrimaryKeyJoinColumn(name="PRTCPNT_TID")            public Administrator extends Participant {            ...            }


注釋 C. 可嵌入對象的一對一關(guān)系
            // Employee (parent) class            @Entity            @Table(name="T_EMPLOYEE")            public class Employee implements Serializable {            @Embedded private EmployeeRecord employeeRec;            ...            }            // EmployeeRecord (child) class            @Embeddable            public class EmployeeRecord implements Serializable {            ...            }


注釋 D. 多對一關(guān)系
            // Address (parent) class            @Entity            @Table(name="T_ADDRESS")            public class Address implements Serializable {            ...            @Column(name="ADDR_TID")            @Id private Long addressId;            }            // Phone (child) class            @Entity            @Table(name="T_PHONE")            public class Phone implements Serializable {            ...            @ManyToOne            @JoinColumn(name="ADDR_TID")            private Address address;            ...            }


注釋 E. 一對多關(guān)系
            // Address (parent) entity            @Entity            @Table(name="T_ADDRESS")            public class Address implements Serializable {            ...            @OneToMany(mappedBy="address", cascade=CascadeType.ALL)            private Set<Phone> phones = new HashSet<Phone>();            ...            @Column(name="ADDR_TID")            @Id private Long addressId;            }            // Phone (child) entity            @Entity            @Table(name="T_PHONE")            public class Phone implements Serializable {            ...            @ManyToOne(cascade=CascadeType.ALL)            @JoinColumn(name="ADDR_TID")            private Address address;            ...            }


注釋 F. 多對多關(guān)系
            // User (owner/child) entity            @Entity            @Table(name="T_USER")            public class User implements Serializable {            @Column(name="USER_TID")            @Id private Long userId;            @ManyToMany            @JoinTable(            name="T_USER_GROUP",            joinColumns=@JoinColumn(name="USER_TID"),            inverseJoinColumns=@JoinColumn(name="GROUP_TID"))            private Set<Group> groups = new HashSet<Group>();            ...            }            // Group (non-owner/parent) entity            @Entity            @Table(name="T_GROUP")            public class Group implements Serializable {            @Column(name="GROUP_TID")            @Id private Long groupId;            @ManyToMany(mapppedBy="groups")            private Set<User> users = new HashSet<User>();            ...            }


注釋 G. 延遲初始化
            // Address (parent) entity            @Entity            @Table(name="T_ADDRESS")            public class Address implements Serializable {            ...            @OneToMany(mappedBy="address", fetch=FetchType.EAGER)            private Set<Phone> phones = new HashSet<Phone>();;            ...            @Column(name="ADDR_TID")            @Id private Long addressId;            }            // Phone (child) entity            @Entity            @Table(name="T_PHONE")            public class Phone implements Serializable {            ...            @ManyToOne            @JoinColumn(name="ADDR_TID")            private Address address;            ...            }


注釋 H. 對象標識
            // Address entity            @Entity            @Table(name="T_ADDRESS")            public class Address implements Serializable {            ...            @SequenceGenerator(name="AddressSeq", sequenceName="T_ADDRESS_SEQ")            @GeneratedValue(            strategy=GenerationType.SEQUENCE,            generator="AddressSeq")            @Column(name="ADDR_TID")            @Id private Long addressId;            ...            }


注釋 I. 樂(lè )觀(guān)鎖定
            @Entity            @Table(name="T_ADDRESS")            public class Address {            ...            @Column(name="VERSION")            @Version private long version;            ...            }

本站僅提供存儲服務(wù),所有內容均由用戶(hù)發(fā)布,如發(fā)現有害或侵權內容,請點(diǎn)擊舉報。
打開(kāi)APP,閱讀全文并永久保存 查看更多類(lèi)似文章
猜你喜歡
類(lèi)似文章
Hibernate day01 day02 day03 day04
傳智播客-jpa與hibernate(1)-實(shí)體和主鍵生成
JPA重整ORM山河
JPA踩坑系列之save方法
hibernate+jpa
JPA EntityManager獲得session、connection
更多類(lèi)似文章 >>
生活服務(wù)
分享 收藏 導長(cháng)圖 關(guān)注 下載文章
綁定賬號成功
后續可登錄賬號暢享VIP特權!
如果VIP功能使用有故障,
可點(diǎn)擊這里聯(lián)系客服!

聯(lián)系客服

欧美性猛交XXXX免费看蜜桃,成人网18免费韩国,亚洲国产成人精品区综合,欧美日韩一区二区三区高清不卡,亚洲综合一区二区精品久久