如果關(guān)心開發(fā)人員的最新熱點,那么您可能聽說過 IOC (控制倒置,Inversion of Control)容器和 AOP (面向方面編程)。不過,像許多開發(fā)人員一樣,您可能不清楚在自己的開發(fā)工作中如何使用這些技術(shù)。在本文中,通過具體介紹使用 Hibernate 和 Spring 在企業(yè)應(yīng)用程序中構(gòu)建一個事務(wù)持久層,您會認識到這些技術(shù)。
Hibernate 是 Java 平臺上的一種流行的、容易使用的開放源代碼對象關(guān)系(OR)映射框架。Spring 是一個 AOP 框架和 IOC 容器。這兩種技術(shù)一起提供了本文中介紹的開發(fā)工作的基礎(chǔ)。將使用 Hibernate 把一些持久性對象映射到關(guān)系數(shù)據(jù)庫中,用 Spring 使 Hibernate 更容易使用并提供聲明性事務(wù)支持。由于為示例類編寫測試代碼時使用了 DbUnit,我還附帶介紹了一點 TDD (測試驅(qū)動的開發(fā))的內(nèi)容。
注意,本文假定讀者熟悉 Java 平臺上的企業(yè)開發(fā),包括 JDBC、OR 映射內(nèi)容、J2EE 設(shè)計模式如 DAO,以及聲明性事務(wù)支持,如 Enterprise JavaBean (EJB)技術(shù)所提供的事務(wù)支持。理解這里的討論不需要成為這些技術(shù)的專家,也不需要熟悉 AOP、IOC 或者 TDD,因為在本文中對這三者都做了介紹。
我將首先介紹兩種開發(fā)技術(shù),然后分析例子。
Hibernate 是 Java 平臺上的一種全功能的、開放源代碼 OR 映射框架。Hibernate 在許多方面類似于 EJB CMP CMR (容器管理的持久性/容器管理的關(guān)系)和 JDO(Java Data Objects)。與 JDO 不同,Hibernate 完全著眼于關(guān)系數(shù)據(jù)庫的 OR 映射,并且包括比大多數(shù)商業(yè)產(chǎn)品更多的功能。大多數(shù) EJB CMP CMR 解決方案使用代碼生成實現(xiàn)持久性代碼,而 JDO 使用字節(jié)碼修飾。與之相反,Hibernate 使用反射和運行時字節(jié)碼生成,使它對于最終用戶幾乎是透明的(以前 Hibernate 的實現(xiàn)只使用反射,它有助于調(diào)試,當前版本保留了這種選項)。
![]() |
|
Hibernate 可以模擬繼承(有幾種方式)、關(guān)聯(lián)(一對一或者一對多、containment 和 aggregation)和 composition。我將在本文中討論每種關(guān)系類型的幾個例子。
Hibernate 提供了一種稱為 Hibernate Query Language (HQL) 的 查詢語言,它類似于 JDO 的 JDOQL 和 EJB 的 EJB QL,盡管它更接近于前者。但是 Hibernate 沒有就此止步:它還可以進行直接的 SQL 查詢和/或使用 object criteria很容易地在運行時構(gòu)成查詢條件。在本文的例子中我將只使用 HQL。
與 EJB CMP CMR 不同,Hibernate 像 JDO 一樣可以在 J2EE 容器內(nèi)部或者外部工作,這可以讓那些進行 TDD 和敏捷開發(fā)的人受益。
![]() ![]() |
![]()
|
AOP 專家 Nicholas Lesiecki 第一次向我解釋 AOP 時,他說的我一個詞也沒理解,我覺得就像第一次考慮使用 IOC 容器的可能性時一樣。每一種技術(shù)的概念基礎(chǔ)本身就需要很好地消化,每一種技術(shù)所使用的各種各樣的縮寫讓事情更糟了——特別是其中許多術(shù)語與我們已經(jīng)使用的根本不一樣了。
像許多技術(shù)一樣,理解這兩種技術(shù)的實際使用比學(xué)習理論更容易。經(jīng)過自己對 AOP 和 IOC 容器實現(xiàn)(即 XWork、PicoContainer 和 Spring)的分析,我發(fā)現(xiàn)這些技術(shù)可以幫助我獲得功能,而不會在多框架中添加基于代碼的依賴性。它們都將成為我后面開發(fā)項目的一部分。
簡單地說,AOP 讓開發(fā)人員可以創(chuàng)建非行為性的關(guān)注點,稱為橫切關(guān)注點,并將它們插入到應(yīng)用程序代碼中。使用 AOP 后,公共服務(wù)(比如日志、持久性、事務(wù)等)就可以分解成方面并應(yīng)用到域?qū)ο笊希瑫r不會增加域?qū)ο蟮膶ο竽P偷膹?fù)雜性。
![]() |
|
IOC 允許創(chuàng)建一個可以構(gòu)造對象的應(yīng)用環(huán)境,然后向這些對象傳遞它們的協(xié)作對象。正如單詞 倒置 所表明的,IOC 就像反過來的 JNDI。沒有使用一堆抽象工廠、服務(wù)定位器、單元素(singleton)和直接構(gòu)造(straight construction),每一個對象都是用其協(xié)作對象構(gòu)造的。因此是由容器管理協(xié)作對象(collaborator)。
Spring 既是一個 AOP 框架、也是一個 IOC 容器。我記得 Grady Booch 說過,對象最好的地方是可以替換它們,而 Spring 最好的地方是它有助于您替換它們。有了 Spring,只要用 JavaBean 屬性和配置文件加入依賴性(協(xié)作對象)。然后可以很容易地在需要時替換具有類似接口的協(xié)作對象。
Spring 為 IOC 容器和 AOP 提供了很好的入口(on-ramp)。因此,不需要熟悉 AOP 就可以理解本文中的例子。所需要知道的就是將要用 AOP 為示例應(yīng)用程序聲明式地添加事務(wù)支持,與使用 EJB 技術(shù)時的方式基本相同。要了解 IOC 容器、AOP 和 Spring 的更多內(nèi)容,請參閱 參考資料 。
![]() ![]() |
![]()
|
在本文的其余部分,所有的討論都將基于一個實際的例子。起點是一個企業(yè)應(yīng)用程序,要為它實現(xiàn)一個事務(wù)持久層。持久層是一個對象關(guān)系數(shù)據(jù)庫,它包括像 User
、 User Group
、 Roles
和 ContactInfo
這些熟悉的抽象。
在深入到數(shù)據(jù)庫的要素——查詢和事務(wù)管理——之前,需要建立它的基礎(chǔ):對象關(guān)系映射。我將用 Hibernate 設(shè)置它,并只使用一點 Spring。
![]() ![]() |
![]()
|
Hibernate 使用 XML ( *.hbm.xml) 文件將 Java 類映射到表,將 JavaBean 屬性映射到數(shù)據(jù)庫表。幸運的是,有一組 XDoclet
標簽支持 Hibernate 開發(fā),這使得創(chuàng)建所需要的 *.hbm.xml 文件更容易了。清單 1 中的代碼將一個 Java 類映射到數(shù)據(jù)庫表。關(guān)于 XDoclet
標簽的更多內(nèi)容,請參閱
參考資料
。
|
可以看到, @hibernate.class table="TBL_USER"
標簽將 User
映射到 TBL_USER
表。 @hibernate.property column="VC_PASSWORD"
將 JavaBean 屬性 password 映射到 VC_PASSWORD
列。 @hibernate.id column="PK_USER_ID"
標簽聲明 id 屬性是主鍵,它將使用本機( generator-class="native"
)數(shù)據(jù)庫機制生成鍵(例如,Oracle sequences 和 SQL Server Identity 鍵)。Hibernate 可以指定 generator-class="native"
以外的、其他可以想象的得到主鍵獲得策略,不過我更愿意使用 native。 type 和 length屬性用于從 Hibernate *.hbm.xml OR 映射文件生成表。這些 final 屬性是可選的,因為使用的可能不是 green-field 數(shù)據(jù)庫。在這個例子中,已經(jīng)有數(shù)據(jù)庫了,所以不需要額外的屬性。( green-field 應(yīng)用程序是一個新的應(yīng)用程序, green-field 數(shù)據(jù)是新應(yīng)用程序的一個新數(shù)據(jù)庫。不會經(jīng)常開發(fā)一個全新的應(yīng)用程序,不過偶爾有一兩次也不錯)。
看過了表如何映射到類以及列如何映射到 JavaBean 屬性,該使用 Hibernate 在 OR 數(shù)據(jù)庫中設(shè)置一些關(guān)系了。
在本節(jié)中,我將只觸及 Hibernate 提供的設(shè)置對象間關(guān)系的選項的一小部分。首先設(shè)置像 User
、 User Group
、 Roles
和 ContactInfo
這些類之間的關(guān)系。其中一些關(guān)系如圖 1 所示,這是數(shù)據(jù)庫的驗證對象模型。
如您所見,在上述抽象中存在各種各樣的關(guān)系。 User
與 ContactInfo
有一對一關(guān)系。 ContactInfo
的生命周期與 User
相同(用數(shù)據(jù)庫的術(shù)語,UML 中的組成 aka 級聯(lián)刪除)。如果刪除 User
,則相應(yīng)的 ContactInfo
也會刪除。在 User
s 與 Role
s 之間存在多對多關(guān)系(即與獨立生命周期相關(guān)聯(lián))。在 Group
s 與 User
s 之間存在一對多關(guān)系,因為組有許多用戶。用戶可以存在于組外,即是 aggregation 而不是 composition (用數(shù)據(jù)庫的說法,在 Group
s 和 Users
之間沒有級聯(lián)刪除關(guān)系)。此外, User
和 Employee
有子類關(guān)系,就是說, Employee
的類型為 User
。表 1 顯示了如何用 XDoclet
標簽創(chuàng)建一些不同類型的對象關(guān)系。
表 1. 用 XDoclet 創(chuàng)建對象關(guān)系
關(guān)系 | Java/XDoclet | SQL DDL(由 Hibernate Schema Export 生成的 MySQL) |
組包含用戶
一對多 Aggregation 雙向 (Group<-->Users) |
[Group.java]
|
|
用戶有聯(lián)系信息
一對一 Composition 單向 (User-->ContactInfo) |
[User.java]
|
create table TBL_USER (
|
用戶與角色關(guān)聯(lián)
多對多 Association 單向 (Users-->Roles) |
[User.java]
|
create table TBL_ROLE (
|
雇員是用戶
Inheritance 用戶 ![]() 雇員 |
|
|
要了解在 Hibernate 中設(shè)置對象關(guān)系的更多內(nèi)容,請參閱 參考資料 。
![]() ![]() |
![]()
|
Hibernate 有三種類型的查詢:
- Criteria, object composition
- SQL
- HQL
在下面的例子中將只使用 HQL。本節(jié)還要使用 Spring,用它的 AOP-driven HibernateTemplate 簡化 Hibernate 會話的處理。在本節(jié)將開發(fā)一個 DAO(Data Access Object)。要了解更多關(guān)于 DAO 的內(nèi)容,請參閱 參考資料 。
清單 2 展示了兩個方法:一個使用 HQL 查詢的組查詢,另一個是后面接一個操作的組查詢。注意在第二個方法中,Spring HibernateTemplate 是如何簡化會話管理的。
|
您可能會注意到第二個方法比第一個方法復(fù)雜得多,因為它強迫加載 users
集合。因為 Group->Users
之間的關(guān)系設(shè)置為 lazy initialize(即表 2 中 lazy="true"
),組對象需要一個活躍的會話以查詢用戶。在定義 Group
和 User
s 之間關(guān)系時設(shè)置這個屬性為 lazy="false"
,則不需要第二個方法。在這種情況下,可能使用第一種方法 ( findGroupByName
) 列出組,用第二種方法( findPopulatedGroupByName
)查看組細節(jié)。
![]() ![]() |
![]()
|
使用 Spring 時,在 J2EE 容器內(nèi)和容器外工作一樣容易。比如在最近的項目中,我在 Eclipse 中,使用 HSQL 和本地數(shù)據(jù)庫對使用 Hibernate 事務(wù)管理器的 Hypersonic SQL 數(shù)據(jù)庫進行持久性單元測試。然后,在部署到 J2EE 服務(wù)器時,將持久層轉(zhuǎn)換為使用 J2EE 數(shù)據(jù)源(通過 JNDI)、JTA 事務(wù)和使用 FireBird (一個開放源代碼版本的 Interbase)。這是用 Spring 作為 IOC 容器完成的。
從清單 3 中可以看出,Spring 允許加入依賴性。注意清單中應(yīng)用程序上下文文件是如何配置 dataSource
的。 dataSource
傳遞給 sessionFactory
, sessionFactory
傳遞給 UserDAO
。
|
設(shè)置了 UserDAO
后,下一步就是定義并使用更多的查詢以展示可以完成的操作。Hibernate 可以用預(yù)定義查詢將查詢存儲到源代碼之外,如清單 4 所示。
|
上述代碼定義了幾個預(yù)定義查詢。 預(yù)定義查詢 是存儲在 *.hbm.xml文件中的查詢。在清單 5 中,可以看到如何執(zhí)行預(yù)定義查詢。
|
查詢進行時,可以在持久層中加上最后一層:使用 Spring 的事務(wù)管理。
![]() ![]() |
![]()
|
Spring 可以聲明式地管理事務(wù)。例如, UserDAO.addUser
方法當前不是在單個事務(wù)中執(zhí)行的。因此,組中的每一個用戶都插入到自己的事務(wù)中,如清單 6 所示。
|
不建議使用上述解決方案,因為每一個 User
都要在自己的事務(wù)中插入到數(shù)據(jù)庫中。如果出現(xiàn)問題,那么只能添加部分用戶。如果希望保留 ACID 屬性(即保證所有都發(fā)生或者所有都不發(fā)生),可以通過程序進行事務(wù)管理,但是它很快就會變得一團糟了。相反,應(yīng)使用 Spring 的 AOP 來支持聲明式的事務(wù),如清單 7 所示。
|
注意在準備清單 7 的代碼時,我重新改寫了 UserDAO
并提取了其接口。這個接口現(xiàn)在是 UserDAO
,它的實現(xiàn)類是 UserDAOImpl
。這樣清單 7 中的事務(wù)代碼就使用了帶有事務(wù)屬性 (PROPAGATION_REQUIRED)
的 UserDAO.addGroup()
方法。現(xiàn)在只要底層數(shù)據(jù)庫支持,就可以在一個事務(wù)中添加所有用戶。
![]() ![]() |
![]()
|
在本文中,介紹了如何使用 Hibernate 和 Spring 實現(xiàn)一個事務(wù)持久層。Hibernate 是一種先進的 OR 映射工具,而 Spring 是一個 AOP 框架和 IOC 容器。這兩種技術(shù)的綜合使用,使得開發(fā)人員可以編寫媲美數(shù)據(jù)庫廠商的代碼,它可以在 J2EE 容器中運行,也可以單獨運行。使用了 DbUnit (JUnit 的擴展)構(gòu)建和測試本文中例子的所有代碼,雖然這不是討論的重點。
要了解有關(guān) AOP、IOC 容器和測試驅(qū)動開發(fā)的更多內(nèi)容,請參閱 參考資料 。
如果關(guān)心開發(fā)人員的最新熱點,那么您可能聽說過 IOC (控制倒置,Inversion of Control)容器和 AOP (面向方面編程)。不過,像許多開發(fā)人員一樣,您可能不清楚在自己的開發(fā)工作中如何使用這些技術(shù)。在本文中,通過具體介紹使用 Hibernate 和 Spring 在企業(yè)應(yīng)用程序中構(gòu)建一個事務(wù)持久層,您會認識到這些技術(shù)。
Hibernate 是 Java 平臺上的一種流行的、容易使用的開放源代碼對象關(guān)系(OR)映射框架。Spring 是一個 AOP 框架和 IOC 容器。這兩種技術(shù)一起提供了本文中介紹的開發(fā)工作的基礎(chǔ)。將使用 Hibernate 把一些持久性對象映射到關(guān)系數(shù)據(jù)庫中,用 Spring 使 Hibernate 更容易使用并提供聲明性事務(wù)支持。由于為示例類編寫測試代碼時使用了 DbUnit,我還附帶介紹了一點 TDD (測試驅(qū)動的開發(fā))的內(nèi)容。
注意,本文假定讀者熟悉 Java 平臺上的企業(yè)開發(fā),包括 JDBC、OR 映射內(nèi)容、J2EE 設(shè)計模式如 DAO,以及聲明性事務(wù)支持,如 Enterprise JavaBean (EJB)技術(shù)所提供的事務(wù)支持。理解這里的討論不需要成為這些技術(shù)的專家,也不需要熟悉 AOP、IOC 或者 TDD,因為在本文中對這三者都做了介紹。
我將首先介紹兩種開發(fā)技術(shù),然后分析例子。
Hibernate 是 Java 平臺上的一種全功能的、開放源代碼 OR 映射框架。Hibernate 在許多方面類似于 EJB CMP CMR (容器管理的持久性/容器管理的關(guān)系)和 JDO(Java Data Objects)。與 JDO 不同,Hibernate 完全著眼于關(guān)系數(shù)據(jù)庫的 OR 映射,并且包括比大多數(shù)商業(yè)產(chǎn)品更多的功能。大多數(shù) EJB CMP CMR 解決方案使用代碼生成實現(xiàn)持久性代碼,而 JDO 使用字節(jié)碼修飾。與之相反,Hibernate 使用反射和運行時字節(jié)碼生成,使它對于最終用戶幾乎是透明的(以前 Hibernate 的實現(xiàn)只使用反射,它有助于調(diào)試,當前版本保留了這種選項)。
![]() |
|
Hibernate 可以模擬繼承(有幾種方式)、關(guān)聯(lián)(一對一或者一對多、containment 和 aggregation)和 composition。我將在本文中討論每種關(guān)系類型的幾個例子。
Hibernate 提供了一種稱為 Hibernate Query Language (HQL) 的 查詢語言,它類似于 JDO 的 JDOQL 和 EJB 的 EJB QL,盡管它更接近于前者。但是 Hibernate 沒有就此止步:它還可以進行直接的 SQL 查詢和/或使用 object criteria很容易地在運行時構(gòu)成查詢條件。在本文的例子中我將只使用 HQL。
與 EJB CMP CMR 不同,Hibernate 像 JDO 一樣可以在 J2EE 容器內(nèi)部或者外部工作,這可以讓那些進行 TDD 和敏捷開發(fā)的人受益。
![]() ![]() |
![]()
|
AOP 專家 Nicholas Lesiecki 第一次向我解釋 AOP 時,他說的我一個詞也沒理解,我覺得就像第一次考慮使用 IOC 容器的可能性時一樣。每一種技術(shù)的概念基礎(chǔ)本身就需要很好地消化,每一種技術(shù)所使用的各種各樣的縮寫讓事情更糟了——特別是其中許多術(shù)語與我們已經(jīng)使用的根本不一樣了。
像許多技術(shù)一樣,理解這兩種技術(shù)的實際使用比學(xué)習理論更容易。經(jīng)過自己對 AOP 和 IOC 容器實現(xiàn)(即 XWork、PicoContainer 和 Spring)的分析,我發(fā)現(xiàn)這些技術(shù)可以幫助我獲得功能,而不會在多框架中添加基于代碼的依賴性。它們都將成為我后面開發(fā)項目的一部分。
簡單地說,AOP 讓開發(fā)人員可以創(chuàng)建非行為性的關(guān)注點,稱為橫切關(guān)注點,并將它們插入到應(yīng)用程序代碼中。使用 AOP 后,公共服務(wù)(比如日志、持久性、事務(wù)等)就可以分解成方面并應(yīng)用到域?qū)ο笊希瑫r不會增加域?qū)ο蟮膶ο竽P偷膹?fù)雜性。
![]() |
|
IOC 允許創(chuàng)建一個可以構(gòu)造對象的應(yīng)用環(huán)境,然后向這些對象傳遞它們的協(xié)作對象。正如單詞 倒置 所表明的,IOC 就像反過來的 JNDI。沒有使用一堆抽象工廠、服務(wù)定位器、單元素(singleton)和直接構(gòu)造(straight construction),每一個對象都是用其協(xié)作對象構(gòu)造的。因此是由容器管理協(xié)作對象(collaborator)。
Spring 既是一個 AOP 框架、也是一個 IOC 容器。我記得 Grady Booch 說過,對象最好的地方是可以替換它們,而 Spring 最好的地方是它有助于您替換它們。有了 Spring,只要用 JavaBean 屬性和配置文件加入依賴性(協(xié)作對象)。然后可以很容易地在需要時替換具有類似接口的協(xié)作對象。
Spring 為 IOC 容器和 AOP 提供了很好的入口(on-ramp)。因此,不需要熟悉 AOP 就可以理解本文中的例子。所需要知道的就是將要用 AOP 為示例應(yīng)用程序聲明式地添加事務(wù)支持,與使用 EJB 技術(shù)時的方式基本相同。要了解 IOC 容器、AOP 和 Spring 的更多內(nèi)容,請參閱 參考資料 。
![]() ![]() |
![]()
|
在本文的其余部分,所有的討論都將基于一個實際的例子。起點是一個企業(yè)應(yīng)用程序,要為它實現(xiàn)一個事務(wù)持久層。持久層是一個對象關(guān)系數(shù)據(jù)庫,它包括像 User
、 User Group
、 Roles
和 ContactInfo
這些熟悉的抽象。
在深入到數(shù)據(jù)庫的要素——查詢和事務(wù)管理——之前,需要建立它的基礎(chǔ):對象關(guān)系映射。我將用 Hibernate 設(shè)置它,并只使用一點 Spring。
![]() ![]() |
![]()
|
Hibernate 使用 XML ( *.hbm.xml) 文件將 Java 類映射到表,將 JavaBean 屬性映射到數(shù)據(jù)庫表。幸運的是,有一組 XDoclet
標簽支持 Hibernate 開發(fā),這使得創(chuàng)建所需要的 *.hbm.xml 文件更容易了。清單 1 中的代碼將一個 Java 類映射到數(shù)據(jù)庫表。關(guān)于 XDoclet
標簽的更多內(nèi)容,請參閱
參考資料
。
|
可以看到, @hibernate.class table="TBL_USER"
標簽將 User
映射到 TBL_USER
表。 @hibernate.property column="VC_PASSWORD"
將 JavaBean 屬性 password 映射到 VC_PASSWORD
列。 @hibernate.id column="PK_USER_ID"
標簽聲明 id 屬性是主鍵,它將使用本機( generator-class="native"
)數(shù)據(jù)庫機制生成鍵(例如,Oracle sequences 和 SQL Server Identity 鍵)。Hibernate 可以指定 generator-class="native"
以外的、其他可以想象的得到主鍵獲得策略,不過我更愿意使用 native。 type 和 length屬性用于從 Hibernate *.hbm.xml OR 映射文件生成表。這些 final 屬性是可選的,因為使用的可能不是 green-field 數(shù)據(jù)庫。在這個例子中,已經(jīng)有數(shù)據(jù)庫了,所以不需要額外的屬性。( green-field 應(yīng)用程序是一個新的應(yīng)用程序, green-field 數(shù)據(jù)是新應(yīng)用程序的一個新數(shù)據(jù)庫。不會經(jīng)常開發(fā)一個全新的應(yīng)用程序,不過偶爾有一兩次也不錯)。
看過了表如何映射到類以及列如何映射到 JavaBean 屬性,該使用 Hibernate 在 OR 數(shù)據(jù)庫中設(shè)置一些關(guān)系了。
在本節(jié)中,我將只觸及 Hibernate 提供的設(shè)置對象間關(guān)系的選項的一小部分。首先設(shè)置像 User
、 User Group
、 Roles
和 ContactInfo
這些類之間的關(guān)系。其中一些關(guān)系如圖 1 所示,這是數(shù)據(jù)庫的驗證對象模型。
如您所見,在上述抽象中存在各種各樣的關(guān)系。 User
與 ContactInfo
有一對一關(guān)系。 ContactInfo
的生命周期與 User
相同(用數(shù)據(jù)庫的術(shù)語,UML 中的組成 aka 級聯(lián)刪除)。如果刪除 User
,則相應(yīng)的 ContactInfo
也會刪除。在 User
s 與 Role
s 之間存在多對多關(guān)系(即與獨立生命周期相關(guān)聯(lián))。在 Group
s 與 User
s 之間存在一對多關(guān)系,因為組有許多用戶。用戶可以存在于組外,即是 aggregation 而不是 composition (用數(shù)據(jù)庫的說法,在 Group
s 和 Users
之間沒有級聯(lián)刪除關(guān)系)。此外, User
和 Employee
有子類關(guān)系,就是說, Employee
的類型為 User
。表 1 顯示了如何用 XDoclet
標簽創(chuàng)建一些不同類型的對象關(guān)系。
表 1. 用 XDoclet 創(chuàng)建對象關(guān)系
關(guān)系 | Java/XDoclet | SQL DDL(由 Hibernate Schema Export 生成的 MySQL) |
組包含用戶
一對多 Aggregation 雙向 (Group<-->Users) |
[Group.java]
|
|
用戶有聯(lián)系信息
一對一 Composition 單向 (User-->ContactInfo) |
[User.java]
|
create table TBL_USER (
|
用戶與角色關(guān)聯(lián)
多對多 Association 單向 (Users-->Roles) |
[User.java]
|
create table TBL_ROLE (
|
雇員是用戶
Inheritance 用戶 ![]() 雇員 |
|
|
要了解在 Hibernate 中設(shè)置對象關(guān)系的更多內(nèi)容,請參閱 參考資料 。
![]() ![]() |
![]()
|
Hibernate 有三種類型的查詢:
- Criteria, object composition
- SQL
- HQL
在下面的例子中將只使用 HQL。本節(jié)還要使用 Spring,用它的 AOP-driven HibernateTemplate 簡化 Hibernate 會話的處理。在本節(jié)將開發(fā)一個 DAO(Data Access Object)。要了解更多關(guān)于 DAO 的內(nèi)容,請參閱 參考資料 。
清單 2 展示了兩個方法:一個使用 HQL 查詢的組查詢,另一個是后面接一個操作的組查詢。注意在第二個方法中,Spring HibernateTemplate 是如何簡化會話管理的。
|
您可能會注意到第二個方法比第一個方法復(fù)雜得多,因為它強迫加載 users
集合。因為 Group->Users
之間的關(guān)系設(shè)置為 lazy initialize(即表 2 中 lazy="true"
),組對象需要一個活躍的會話以查詢用戶。在定義 Group
和 User
s 之間關(guān)系時設(shè)置這個屬性為 lazy="false"
,則不需要第二個方法。在這種情況下,可能使用第一種方法 ( findGroupByName
) 列出組,用第二種方法( findPopulatedGroupByName
)查看組細節(jié)。
![]() ![]() |
![]()
|
使用 Spring 時,在 J2EE 容器內(nèi)和容器外工作一樣容易。比如在最近的項目中,我在 Eclipse 中,使用 HSQL 和本地數(shù)據(jù)庫對使用 Hibernate 事務(wù)管理器的 Hypersonic SQL 數(shù)據(jù)庫進行持久性單元測試。然后,在部署到 J2EE 服務(wù)器時,將持久層轉(zhuǎn)換為使用 J2EE 數(shù)據(jù)源(通過 JNDI)、JTA 事務(wù)和使用 FireBird (一個開放源代碼版本的 Interbase)。這是用 Spring 作為 IOC 容器完成的。
從清單 3 中可以看出,Spring 允許加入依賴性。注意清單中應(yīng)用程序上下文文件是如何配置 dataSource
的。 dataSource
傳遞給 sessionFactory
, sessionFactory
傳遞給 UserDAO
。
|
設(shè)置了 UserDAO
后,下一步就是定義并使用更多的查詢以展示可以完成的操作。Hibernate 可以用預(yù)定義查詢將查詢存儲到源代碼之外,如清單 4 所示。
|
上述代碼定義了幾個預(yù)定義查詢。 預(yù)定義查詢 是存儲在 *.hbm.xml文件中的查詢。在清單 5 中,可以看到如何執(zhí)行預(yù)定義查詢。
|
查詢進行時,可以在持久層中加上最后一層:使用 Spring 的事務(wù)管理。
![]() ![]() |
![]()
|
Spring 可以聲明式地管理事務(wù)。例如, UserDAO.addUser
方法當前不是在單個事務(wù)中執(zhí)行的。因此,組中的每一個用戶都插入到自己的事務(wù)中,如清單 6 所示。
|
不建議使用上述解決方案,因為每一個 User
都要在自己的事務(wù)中插入到數(shù)據(jù)庫中。如果出現(xiàn)問題,那么只能添加部分用戶。如果希望保留 ACID 屬性(即保證所有都發(fā)生或者所有都不發(fā)生),可以通過程序進行事務(wù)管理,但是它很快就會變得一團糟了。相反,應(yīng)使用 Spring 的 AOP 來支持聲明式的事務(wù),如清單 7 所示。
|
注意在準備清單 7 的代碼時,我重新改寫了 UserDAO
并提取了其接口。這個接口現(xiàn)在是 UserDAO
,它的實現(xiàn)類是 UserDAOImpl
。這樣清單 7 中的事務(wù)代碼就使用了帶有事務(wù)屬性 (PROPAGATION_REQUIRED)
的 UserDAO.addGroup()
方法。現(xiàn)在只要底層數(shù)據(jù)庫支持,就可以在一個事務(wù)中添加所有用戶。
![]() ![]() |
![]()
|
在本文中,介紹了如何使用 Hibernate 和 Spring 實現(xiàn)一個事務(wù)持久層。Hibernate 是一種先進的 OR 映射工具,而 Spring 是一個 AOP 框架和 IOC 容器。這兩種技術(shù)的綜合使用,使得開發(fā)人員可以編寫媲美數(shù)據(jù)庫廠商的代碼,它可以在 J2EE 容器中運行,也可以單獨運行。使用了 DbUnit (JUnit 的擴展)構(gòu)建和測試本文中例子的所有代碼,雖然這不是討論的重點。
要了解有關(guān) AOP、IOC 容器和測試驅(qū)動開發(fā)的更多內(nèi)容,請參閱 參考資料 。
如果關(guān)心開發(fā)人員的最新熱點,那么您可能聽說過 IOC (控制倒置,Inversion of Control)容器和 AOP (面向方面編程)。不過,像許多開發(fā)人員一樣,您可能不清楚在自己的開發(fā)工作中如何使用這些技術(shù)。在本文中,通過具體介紹使用 Hibernate 和 Spring 在企業(yè)應(yīng)用程序中構(gòu)建一個事務(wù)持久層,您會認識到這些技術(shù)。
Hibernate 是 Java 平臺上的一種流行的、容易使用的開放源代碼對象關(guān)系(OR)映射框架。Spring 是一個 AOP 框架和 IOC 容器。這兩種技術(shù)一起提供了本文中介紹的開發(fā)工作的基礎(chǔ)。將使用 Hibernate 把一些持久性對象映射到關(guān)系數(shù)據(jù)庫中,用 Spring 使 Hibernate 更容易使用并提供聲明性事務(wù)支持。由于為示例類編寫測試代碼時使用了 DbUnit,我還附帶介紹了一點 TDD (測試驅(qū)動的開發(fā))的內(nèi)容。
注意,本文假定讀者熟悉 Java 平臺上的企業(yè)開發(fā),包括 JDBC、OR 映射內(nèi)容、J2EE 設(shè)計模式如 DAO,以及聲明性事務(wù)支持,如 Enterprise JavaBean (EJB)技術(shù)所提供的事務(wù)支持。理解這里的討論不需要成為這些技術(shù)的專家,也不需要熟悉 AOP、IOC 或者 TDD,因為在本文中對這三者都做了介紹。
我將首先介紹兩種開發(fā)技術(shù),然后分析例子。
Hibernate 是 Java 平臺上的一種全功能的、開放源代碼 OR 映射框架。Hibernate 在許多方面類似于 EJB CMP CMR (容器管理的持久性/容器管理的關(guān)系)和 JDO(Java Data Objects)。與 JDO 不同,Hibernate 完全著眼于關(guān)系數(shù)據(jù)庫的 OR 映射,并且包括比大多數(shù)商業(yè)產(chǎn)品更多的功能。大多數(shù) EJB CMP CMR 解決方案使用代碼生成實現(xiàn)持久性代碼,而 JDO 使用字節(jié)碼修飾。與之相反,Hibernate 使用反射和運行時字節(jié)碼生成,使它對于最終用戶幾乎是透明的(以前 Hibernate 的實現(xiàn)只使用反射,它有助于調(diào)試,當前版本保留了這種選項)。
![]() |
|
Hibernate 可以模擬繼承(有幾種方式)、關(guān)聯(lián)(一對一或者一對多、containment 和 aggregation)和 composition。我將在本文中討論每種關(guān)系類型的幾個例子。
Hibernate 提供了一種稱為 Hibernate Query Language (HQL) 的 查詢語言,它類似于 JDO 的 JDOQL 和 EJB 的 EJB QL,盡管它更接近于前者。但是 Hibernate 沒有就此止步:它還可以進行直接的 SQL 查詢和/或使用 object criteria很容易地在運行時構(gòu)成查詢條件。在本文的例子中我將只使用 HQL。
與 EJB CMP CMR 不同,Hibernate 像 JDO 一樣可以在 J2EE 容器內(nèi)部或者外部工作,這可以讓那些進行 TDD 和敏捷開發(fā)的人受益。
![]() ![]() |
![]()
|
AOP 專家 Nicholas Lesiecki 第一次向我解釋 AOP 時,他說的我一個詞也沒理解,我覺得就像第一次考慮使用 IOC 容器的可能性時一樣。每一種技術(shù)的概念基礎(chǔ)本身就需要很好地消化,每一種技術(shù)所使用的各種各樣的縮寫讓事情更糟了——特別是其中許多術(shù)語與我們已經(jīng)使用的根本不一樣了。
像許多技術(shù)一樣,理解這兩種技術(shù)的實際使用比學(xué)習理論更容易。經(jīng)過自己對 AOP 和 IOC 容器實現(xiàn)(即 XWork、PicoContainer 和 Spring)的分析,我發(fā)現(xiàn)這些技術(shù)可以幫助我獲得功能,而不會在多框架中添加基于代碼的依賴性。它們都將成為我后面開發(fā)項目的一部分。
簡單地說,AOP 讓開發(fā)人員可以創(chuàng)建非行為性的關(guān)注點,稱為橫切關(guān)注點,并將它們插入到應(yīng)用程序代碼中。使用 AOP 后,公共服務(wù)(比如日志、持久性、事務(wù)等)就可以分解成方面并應(yīng)用到域?qū)ο笊希瑫r不會增加域?qū)ο蟮膶ο竽P偷膹?fù)雜性。
![]() |
|
IOC 允許創(chuàng)建一個可以構(gòu)造對象的應(yīng)用環(huán)境,然后向這些對象傳遞它們的協(xié)作對象。正如單詞 倒置 所表明的,IOC 就像反過來的 JNDI。沒有使用一堆抽象工廠、服務(wù)定位器、單元素(singleton)和直接構(gòu)造(straight construction),每一個對象都是用其協(xié)作對象構(gòu)造的。因此是由容器管理協(xié)作對象(collaborator)。
Spring 既是一個 AOP 框架、也是一個 IOC 容器。我記得 Grady Booch 說過,對象最好的地方是可以替換它們,而 Spring 最好的地方是它有助于您替換它們。有了 Spring,只要用 JavaBean 屬性和配置文件加入依賴性(協(xié)作對象)。然后可以很容易地在需要時替換具有類似接口的協(xié)作對象。
Spring 為 IOC 容器和 AOP 提供了很好的入口(on-ramp)。因此,不需要熟悉 AOP 就可以理解本文中的例子。所需要知道的就是將要用 AOP 為示例應(yīng)用程序聲明式地添加事務(wù)支持,與使用 EJB 技術(shù)時的方式基本相同。要了解 IOC 容器、AOP 和 Spring 的更多內(nèi)容,請參閱 參考資料 。
![]() ![]() |
![]()
|
在本文的其余部分,所有的討論都將基于一個實際的例子。起點是一個企業(yè)應(yīng)用程序,要為它實現(xiàn)一個事務(wù)持久層。持久層是一個對象關(guān)系數(shù)據(jù)庫,它包括像 User
、 User Group
、 Roles
和 ContactInfo
這些熟悉的抽象。
在深入到數(shù)據(jù)庫的要素——查詢和事務(wù)管理——之前,需要建立它的基礎(chǔ):對象關(guān)系映射。我將用 Hibernate 設(shè)置它,并只使用一點 Spring。
![]() ![]() |
![]()
|
Hibernate 使用 XML ( *.hbm.xml) 文件將 Java 類映射到表,將 JavaBean 屬性映射到數(shù)據(jù)庫表。幸運的是,有一組 XDoclet
標簽支持 Hibernate 開發(fā),這使得創(chuàng)建所需要的 *.hbm.xml 文件更容易了。清單 1 中的代碼將一個 Java 類映射到數(shù)據(jù)庫表。關(guān)于 XDoclet
標簽的更多內(nèi)容,請參閱
參考資料
。
|
可以看到, @hibernate.class table="TBL_USER"
標簽將 User
映射到 TBL_USER
表。 @hibernate.property column="VC_PASSWORD"
將 JavaBean 屬性 password 映射到 VC_PASSWORD
列。 @hibernate.id column="PK_USER_ID"
標簽聲明 id 屬性是主鍵,它將使用本機( generator-class="native"
)數(shù)據(jù)庫機制生成鍵(例如,Oracle sequences 和 SQL Server Identity 鍵)。Hibernate 可以指定 generator-class="native"
以外的、其他可以想象的得到主鍵獲得策略,不過我更愿意使用 native。 type 和 length屬性用于從 Hibernate *.hbm.xml OR 映射文件生成表。這些 final 屬性是可選的,因為使用的可能不是 green-field 數(shù)據(jù)庫。在這個例子中,已經(jīng)有數(shù)據(jù)庫了,所以不需要額外的屬性。( green-field 應(yīng)用程序是一個新的應(yīng)用程序, green-field 數(shù)據(jù)是新應(yīng)用程序的一個新數(shù)據(jù)庫。不會經(jīng)常開發(fā)一個全新的應(yīng)用程序,不過偶爾有一兩次也不錯)。
看過了表如何映射到類以及列如何映射到 JavaBean 屬性,該使用 Hibernate 在 OR 數(shù)據(jù)庫中設(shè)置一些關(guān)系了。
在本節(jié)中,我將只觸及 Hibernate 提供的設(shè)置對象間關(guān)系的選項的一小部分。首先設(shè)置像 User
、 User Group
、 Roles
和 ContactInfo
這些類之間的關(guān)系。其中一些關(guān)系如圖 1 所示,這是數(shù)據(jù)庫的驗證對象模型。
如您所見,在上述抽象中存在各種各樣的關(guān)系。 User
與 ContactInfo
有一對一關(guān)系。 ContactInfo
的生命周期與 User
相同(用數(shù)據(jù)庫的術(shù)語,UML 中的組成 aka 級聯(lián)刪除)。如果刪除 User
,則相應(yīng)的 ContactInfo
也會刪除。在 User
s 與 Role
s 之間存在多對多關(guān)系(即與獨立生命周期相關(guān)聯(lián))。在 Group
s 與 User
s 之間存在一對多關(guān)系,因為組有許多用戶。用戶可以存在于組外,即是 aggregation 而不是 composition (用數(shù)據(jù)庫的說法,在 Group
s 和 Users
之間沒有級聯(lián)刪除關(guān)系)。此外, User
和 Employee
有子類關(guān)系,就是說, Employee
的類型為 User
。表 1 顯示了如何用 XDoclet
標簽創(chuàng)建一些不同類型的對象關(guān)系。
表 1. 用 XDoclet 創(chuàng)建對象關(guān)系
關(guān)系 | Java/XDoclet | SQL DDL(由 Hibernate Schema Export 生成的 MySQL) |
組包含用戶
一對多 Aggregation 雙向 (Group<-->Users) |
[Group.java]
|
|
用戶有聯(lián)系信息
一對一 Composition 單向 (User-->ContactInfo) |
[User.java]
|
create table TBL_USER (
|
用戶與角色關(guān)聯(lián)
多對多 Association 單向 (Users-->Roles) |
[User.java]
|
create table TBL_ROLE (
|
雇員是用戶
Inheritance 用戶 ![]() 雇員 |
|
|
要了解在 Hibernate 中設(shè)置對象關(guān)系的更多內(nèi)容,請參閱 參考資料 。
![]() ![]() |
![]()
|
Hibernate 有三種類型的查詢:
- Criteria, object composition
- SQL
- HQL
在下面的例子中將只使用 HQL。本節(jié)還要使用 Spring,用它的 AOP-driven HibernateTemplate 簡化 Hibernate 會話的處理。在本節(jié)將開發(fā)一個 DAO(Data Access Object)。要了解更多關(guān)于 DAO 的內(nèi)容,請參閱 參考資料 。
清單 2 展示了兩個方法:一個使用 HQL 查詢的組查詢,另一個是后面接一個操作的組查詢。注意在第二個方法中,Spring HibernateTemplate 是如何簡化會話管理的。
|
您可能會注意到第二個方法比第一個方法復(fù)雜得多,因為它強迫加載 users
集合。因為 Group->Users
之間的關(guān)系設(shè)置為 lazy initialize(即表 2 中 lazy="true"
),組對象需要一個活躍的會話以查詢用戶。在定義 Group
和 User
s 之間關(guān)系時設(shè)置這個屬性為 lazy="false"
,則不需要第二個方法。在這種情況下,可能使用第一種方法 ( findGroupByName
) 列出組,用第二種方法( findPopulatedGroupByName
)查看組細節(jié)。
![]() ![]() |
![]()
|
使用 Spring 時,在 J2EE 容器內(nèi)和容器外工作一樣容易。比如在最近的項目中,我在 Eclipse 中,使用 HSQL 和本地數(shù)據(jù)庫對使用 Hibernate 事務(wù)管理器的 Hypersonic SQL 數(shù)據(jù)庫進行持久性單元測試。然后,在部署到 J2EE 服務(wù)器時,將持久層轉(zhuǎn)換為使用 J2EE 數(shù)據(jù)源(通過 JNDI)、JTA 事務(wù)和使用 FireBird (一個開放源代碼版本的 Interbase)。這是用 Spring 作為 IOC 容器完成的。
從清單 3 中可以看出,Spring 允許加入依賴性。注意清單中應(yīng)用程序上下文文件是如何配置 dataSource
的。 dataSource
傳遞給 sessionFactory
, sessionFactory
傳遞給 UserDAO
。
|
設(shè)置了 UserDAO
后,下一步就是定義并使用更多的查詢以展示可以完成的操作。Hibernate 可以用預(yù)定義查詢將查詢存儲到源代碼之外,如清單 4 所示。
|
上述代碼定義了幾個預(yù)定義查詢。 預(yù)定義查詢 是存儲在 *.hbm.xml文件中的查詢。在清單 5 中,可以看到如何執(zhí)行預(yù)定義查詢。
|
查詢進行時,可以在持久層中加上最后一層:使用 Spring 的事務(wù)管理。
![]() ![]() |
![]()
|
Spring 可以聲明式地管理事務(wù)。例如, UserDAO.addUser
方法當前不是在單個事務(wù)中執(zhí)行的。因此,組中的每一個用戶都插入到自己的事務(wù)中,如清單 6 所示。
|
不建議使用上述解決方案,因為每一個 User
都要在自己的事務(wù)中插入到數(shù)據(jù)庫中。如果出現(xiàn)問題,那么只能添加部分用戶。如果希望保留 ACID 屬性(即保證所有都發(fā)生或者所有都不發(fā)生),可以通過程序進行事務(wù)管理,但是它很快就會變得一團糟了。相反,應(yīng)使用 Spring 的 AOP 來支持聲明式的事務(wù),如清單 7 所示。
|
注意在準備清單 7 的代碼時,我重新改寫了 UserDAO
并提取了其接口。這個接口現(xiàn)在是 UserDAO
,它的實現(xiàn)類是 UserDAOImpl
。這樣清單 7 中的事務(wù)代碼就使用了帶有事務(wù)屬性 (PROPAGATION_REQUIRED)
的 UserDAO.addGroup()
方法。現(xiàn)在只要底層數(shù)據(jù)庫支持,就可以在一個事務(wù)中添加所有用戶。
![]() ![]() |
![]()
|
在本文中,介紹了如何使用 Hibernate 和 Spring 實現(xiàn)一個事務(wù)持久層。Hibernate 是一種先進的 OR 映射工具,而 Spring 是一個 AOP 框架和 IOC 容器。這兩種技術(shù)的綜合使用,使得開發(fā)人員可以編寫媲美數(shù)據(jù)庫廠商的代碼,它可以在 J2EE 容器中運行,也可以單獨運行。使用了 DbUnit (JUnit 的擴展)構(gòu)建和測試本文中例子的所有代碼,雖然這不是討論的重點。
要了解有關(guān) AOP、IOC 容器和測試驅(qū)動開發(fā)的更多內(nèi)容,請參閱 參考資料 。