隨筆-7  評論-13  文章-2  trackbacks-0

          當您自以為已經(jīng)了解了所有開發(fā)工具時,肯定又會冒出一個新的工具。在本文中,developerWorks 的固定撰稿人 Rick Hightower 用一個真實世界的例子向您介紹兩個最激動人心的企業(yè)新技術。Hibernate是一個對象關系映射工具,而 Spring是一個 AOP 框架和 IOC 容器。Rick 介紹了如何結(jié)合這兩者,為企業(yè)應用程序構(gòu)建一個事務持久層。

            如果關心開發(fā)人員的最新熱點,那么您可能聽說過 IOC (控制倒置,Inversion of Control)容器和 AOP (面向方面編程)。不過,像許多開發(fā)人員一樣,您可能不清楚在自己的開發(fā)工作中如何使用這些技術。在本文中,通過具體介紹使用 Hibernate Spring 在企業(yè)應用程序中構(gòu)建一個事務持久層,您會認識到這些技術。

            Hibernate Java 平臺上的一種流行的、容易使用的開放源代碼對象關系(OR)映射框架。Spring 是一個 AOP 框架和 IOC 容器。這兩種技術一起提供了本文中介紹的開發(fā)工作的基礎。將使用 Hibernate 把一些持久性對象映射到關系數(shù)據(jù)庫中,用 Spring 使 Hibernate 更容易使用并提供聲明性事務支持。由于為示例類編寫測試代碼時使用了 DbUnit,我還附帶介紹了一點 TDD (測試驅(qū)動的開發(fā))的內(nèi)容。

            注意,本文假定讀者熟悉 Java 平臺上的企業(yè)開發(fā),包括 JDBCOR 映射內(nèi)容、J2EE 設計模式如 DAO,以及聲明性事務支持,如 Enterprise JavaBean EJB)技術所提供的事務支持。理解這里的討論不需要成為這些技術的專家,也不需要熟悉 AOPIOC 或者 TDD,因為在本文中對這三者都做了介紹。

            我將首先介紹兩種開發(fā)技術,然后分析例子。

            Hibernate 簡介

            Hibernate Java 平臺上的一種全功能的、開放源代碼 OR 映射框架。Hibernate 在許多方面類似于 EJB CMP CMR (容器管理的持久性/容器管理的關系)和 JDOJava Data Objects)。與 JDO 不同,Hibernate 完全著眼于關系數(shù)據(jù)庫的 OR 映射,并且包括比大多數(shù)商業(yè)產(chǎn)品更多的功能。大多數(shù) EJB CMP CMR 解決方案使用代碼生成實現(xiàn)持久性代碼,而 JDO 使用字節(jié)碼修飾。與之相反,Hibernate 使用反射和運行時字節(jié)碼生成,使它對于最終用戶幾乎是透明的(以前 Hibernate 的實現(xiàn)只使用反射,它有助于調(diào)試,當前版本保留了這種選項)。

            移植基于 Hibernate 的應用程序

            如果應用程序必須在多個 RDBMS 系統(tǒng)上運行 ,那么基于 Hibernate 的應用程序可以毫不費力地移植到 IBM DB2MySQLPostgreSQLSybaseOracleHypersonicSQL 和許多其他數(shù)據(jù)庫。我最近甚至將一個應用程序從 MySQL 移植到 Hibernate 沒有很好支持的 Firebird,而這種移植是很容易的。
          Hibernate
          可以模擬繼承(有幾種方式)、關聯(lián)(一對一或者一對多、containment aggregation)和 composition。我將在本文中討論每種關系類型的幾個例子。

            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ā)的人受益。

             Spring 簡介

            AOP 專家 Nicholas Lesiecki 第一次向我解釋 AOP 時,他說的我一個詞也沒理解,我覺得就像第一次考慮使用 IOC 容器的可能性時一樣。每一種技術的概念基礎本身就需要很好地消化,每一種技術所使用的各種各樣的縮寫讓事情更糟了——特別是其中許多術語與我們已經(jīng)使用的根本不一樣了。

            像許多技術一樣,理解這兩種技術的實際使用比學習理論更容易。經(jīng)過自己對 AOP IOC 容器實現(xiàn)(即 XWorkPicoContainer Spring)的分析,我發(fā)現(xiàn)這些技術可以幫助我獲得功能,而不會在多框架中添加基于代碼的依賴性。它們都將成為我后面開發(fā)項目的一部分。

            簡單地說,AOP 讓開發(fā)人員可以創(chuàng)建非行為性的關注點,稱為橫切關注點,并將它們插入到應用程序代碼中。使用 AOP 后,公共服務(比如日志、持久性、事務等)就可以分解成方面并應用到域?qū)ο笊希瑫r不會增加域?qū)ο蟮膶ο竽P偷膹碗s性。

            IOC 允許創(chuàng)建一個可以構(gòu)造對象的應用環(huán)境,然后向這些對象傳遞它們的協(xié)作對象。正如單詞 倒置 所表明的,IOC 就像反過來的 JNDI。沒有使用一堆抽象工廠、服務定位器、單元素(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 為示例應用程序聲明式地添加事務支持,與使用 EJB 技術時的方式基本相同。

             具體到業(yè)務

            在本文的其余部分,所有的討論都將基于一個實際的例子。起點是一個企業(yè)應用程序,要為它實現(xiàn)一個事務持久層。持久層是一個對象關系數(shù)據(jù)庫,它包括像 UserUser GroupRoles ContactInfo 這些熟悉的抽象。

            在深入到數(shù)據(jù)庫的要素——查詢和事務管理——之前,需要建立它的基礎:對象關系映射。我將用 Hibernate 設置它,并只使用一點 Spring

             Hibernate 進行 OR 映射

            Hibernate 使用 XML (*.hbm.xml) 文件將 Java 類映射到表,將 JavaBean 屬性映射到數(shù)據(jù)庫表。幸運的是,有一組 XDoclet 標簽支持 Hibernate 開發(fā),這使得創(chuàng)建所需要的 *.hbm.xml 文件更容易了。清單 1 中的代碼將一個 Java 類映射到數(shù)據(jù)庫表。

            清單 1. Java 類映射到 DB
            [User.java]

          /**
          ?* @hibernate.class table="TBL_USER"
          ?* ..
          ?* ..
          ?* ...
          ?*/
          public class User {

          ?private Long id = new Long(-1);
          ?private String email;
          ?private String password;
          ?
          ?.
          ?.
          ?.

          ?/**
          ? * @return
          ? * @hibernate.id column="PK_USER_ID"
          ? * unsaved-value="-1"
          ? * generator-class="native"?
          ? */
          ?public Long getId() {
          ??return id;
          ?}

          ?...

          ?/**
          ? * @hibernate.property column="VC_EMAIL"
          ? * type="string"
          ? * update="false"
          ? * insert="true"
          ? * unique="true"
          ? * not-null="true"
          ? * length="82"
          ? * @return
          ? */
          ?public String getEmail() {
          ??return email;
          ?}

          ?/**
          ? * @hibernate.property column="VC_PASSWORD"
          ? * type="string"
          ? * update="false"
          ? * insert="true"
          ? * unique="true"
          ? * not-null="true"
          ? * length="20"
          ? * @return
          ? */
          ?public String getPassword() {
          ??return password;
          ?}

          ?...
          ?...
          ?...
          }

            可以看到,@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" 以外的、其他可以想象的得到主鍵獲得策略,不過我更愿意使用 nativetype length 屬性用于從 Hibernate *.hbm.xml OR 映射文件生成表。這些 final 屬性是可選的,因為使用的可能不是 green-field 數(shù)據(jù)庫。在這個例子中,已經(jīng)有數(shù)據(jù)庫了,所以不需要額外的屬性。(green-field 應用程序 是一個新的應用程序, green-field 數(shù)據(jù)是新應用程序的一個新數(shù)據(jù)庫。不會經(jīng)常開發(fā)一個全新的應用程序,不過偶爾有一兩次也不錯)。

            看過了表如何映射到類以及列如何映射到 JavaBean 屬性,該使用 Hibernate OR 數(shù)據(jù)庫中設置一些關系了。

             設置對象關系

            在本節(jié)中,我將只觸及 Hibernate 提供的設置對象間關系的選項的一小部分。首先設置像 UserUser GroupRoles ContactInfo 這些類之間的關系。其中一些關系如圖 1 所示,這是數(shù)據(jù)庫的驗證對象模型。


          1. 關系的圖示

            如您所見,在上述抽象中存在各種各樣的關系。User ContactInfo 有一對一關系。ContactInfo 的生命周期與 User 相同(用數(shù)據(jù)庫的術語,UML 中的組成 aka 級聯(lián)刪除)。如果刪除 User,則相應的 ContactInfo 也會刪除。在 Users Roles 之間存在多對多關系(即與獨立生命周期相關聯(lián))。在 Groups Users 之間存在一對多關系,因為組有許多用戶。用戶可以存在于組外,即是 aggregation 而不是 composition (用數(shù)據(jù)庫的說法,在 Groups Users 之間沒有級聯(lián)刪除關系)。此外,User Employee 有子類關系,就是說,Employee 的類型為 User。表 1 顯示了如何用 XDoclet 標簽創(chuàng)建一些不同類型的對象關系。

          1. XDoclet 創(chuàng)建對象關系

          關系

          Java/XDoclet

          SQL DDL (由 Hibernate Schema Export 生成的 MySQL

          組包含用戶
          一對多
          Aggregation
          雙向
          (Group<-->Users)

          [Group.java]
          /**
          *
          * @return
          *
          * @hibernate.bag name="users"
          * cascade="save-update"
          * lazy="true"
          * inverse="true"
          *
          * @hibernate.collection-key
          * column="FK_GROUP_ID"
          *
          * @hibernate.collection-one-to-many
          * class="net.sf.hibernateExamples.User"
          */
          public List getUsers() {
          return users;
          }

          [User.java]
          /**
          * @hibernate.many-to-one
          * column="FK_GROUP_ID"
          * class="net.sf.hibernateExamples.Group"
          */
          public Group getGroup() {
          return group;
          }

          create table TBL_USER (
          PK_USER_ID BIGINT NOT NULL AUTO_INCREMENT,
          USER_TYPE VARCHAR(255) not null,
          FK_GROUP_ID BIGINT,
          VC_EMAIL VARCHAR(82) not null unique,
          primary key (PK_USER_ID)
          )


          create table TBL_GROUP (
          PK_GROUP_ID BIGINT NOT NULL AUTO_INCREMENT,
          VC_DESCRIPTION VARCHAR(255),
          VC_NAME VARCHAR(40) unique,
          primary key (PK_GROUP_ID)
          )

          alter table TBL_USER add index (FK_GROUP_ID),
          add constraint FK_111 foreign key (FK_GROUP_ID)
          references TBL_GROUP (PK_GROUP_ID)

          用戶有聯(lián)系信息
          一對一
          Composition
          單向
          (User-->ContactInfo)

          [User.java]
          /**
          * @return
          *
          * @hibernate.one-to-one cascade="all"
          *
          */
          public ContactInfo getContactInfo() {
          return contactInfo;
          }

          [ContactInfo.java]
          (Nothing to see here. Unidirectional!)

          create table TBL_USER (
          PK_USER_ID BIGINT NOT NULL AUTO_INCREMENT,
          USER_TYPE VARCHAR(255) not null,
          FK_GROUP_ID BIGINT,
          VC_EMAIL VARCHAR(82) not null unique,
          primary key (PK_USER_ID)
          )

          create table TBL_CONTACT_INFO (
          PK_CONTACT_INFO_ID BIGINT not null,
          ...
          ...
          ...
          primary key (PK_CONTACT_INFO_ID)
          )

          用戶與角色關聯(lián)
          多對多
          Association
          單向
          (Users-->Roles)

          [User.java]
          /**
          * @return
          * @hibernate.bag
          * table="TBL_JOIN_USER_ROLE"
          * cascade="all"
          * inverse="true"
          *
          * @hibernate.collection-key
          * column="FK_USER_ID"
          *
          * @hibernate.collection-many-to-many
          * class="net.sf.hibernateExamples.Role"
          * column="FK_ROLE_ID"
          *
          */
          public List getRoles() {
          return roles;
          }

          [Role.java]
          Nothing to see here. Unidirectional!

          create table TBL_ROLE (
          PK_ROLE_ID BIGINT NOT NULL AUTO_INCREMENT,
          VC_DESCRIPTION VARCHAR(200),
          VC_NAME VARCHAR(20),
          primary key (PK_ROLE_ID)
          )

          create table TBL_USER (
          PK_USER_ID BIGINT NOT NULL AUTO_INCREMENT,
          USER_TYPE VARCHAR(255) not null,
          FK_GROUP_ID BIGINT,
          VC_EMAIL VARCHAR(82) not null unique,
          primary key (PK_USER_ID)
          )

          create table TBL_JOIN_USER_ROLE (
          FK_USER_ID BIGINT not null,
          FK_ROLE_ID BIGINT not null
          )

          雇員是用戶
          Inheritance
          用戶
          雇員

          [User.java]
          /**
          * @hibernate.class table="TBL_USER"
          * discriminator-value="2"
          * @hibernate.discriminator column="USER_TYPE"
          *
          ...
          ...
          ...
          */
          public class User {

          [Employee.java]
          /**
          * @hibernate.subclass discriminator-value = "1"
          */
          public class Employee extends User{

          create table TBL_USER (
          PK_USER_ID BIGINT NOT NULL AUTO_INCREMENT,
          USER_TYPE VARCHAR(255) not null,
          FK_GROUP_ID BIGINT,
          VC_EMAIL VARCHAR(82) not null unique,
          primary key (PK_USER_ID)
          )

             Hibernate 中的查詢

            Hibernate 有三種類型的查詢:

            ☆ Criteria, object composition
            ☆ SQL
            ☆ HQL

            在下面的例子中將只使用 HQL。本節(jié)還要使用 Spring,用它的 AOP-driven HibernateTemplate 簡化 Hibernate 會話的處理。在本節(jié)將開發(fā)一個 DAOData Access Object)。要了解更多關于 DAO 的內(nèi)容,請參閱 參考資料。

            清單 2 展示了兩個方法:一個使用 HQL 查詢的組查詢,另一個是后面接一個操作的組查詢。注意在第二個方法中,Spring HibernateTemplate 是如何簡化會話管理的。

            清單 2. 使用查詢

          import net.sf.hibernate.HibernateException;
          import net.sf.hibernate.Session;
          import net.sf.hibernate.Query;
          import org.springframework.orm.hibernate.HibernateCallback;
          import org.springframework.orm.hibernate.support.HibernateDaoSupport;

          /**
          ?* @author Richard Hightower
          ?* ArcMind Inc. http://www.arc-mind.com
          ?*/
          public class UserDAO extends HibernateDaoSupport{

          ? .
          ? .
          ? .

          ? /**
          ?* Demonstrates looking up a group with a HQL query
          ?* @param email
          ?* @return
          ?*/?
          ?public Group findGroupByName(String name) {
          ??? return (Group) getHibernateTemplate().find("from Group g where g.name=?",name).get(0);
          ?}
          ?
          ?/**
          ? * Demonstrates looking up a group and forcing it to populate users (relationship was lazy)
          ? * @param email
          ? * @return
          ? */?
          ?public Group findPopulatedGroupByName(final String name) {
          ??? HibernateCallback callback = new HibernateCallback(){

          ???? public Object doInHibernate(Session session) throws HibernateException, SQLException {
          ???Group group =null;
          ???String query = "from Group g where g.name=?";
          ???Query queryObject = getHibernateTemplate().createQuery(session, query);
          ???queryObject.setParameter(0, name);
          ???group = (Group) queryObject.list().get(0);
          ???group.getUsers().size();//force load
          ???return group;
          ???}
          ???
          ??};
          ??
          ??return (Group) getHibernateTemplate().execute(callback);
          ?}
          ? .
          ? .
          ? .
          }???

            您可能會注意到第二個方法比第一個方法復雜得多,因為它強迫加載 users 集合。因為 Group->Users 之間的關系設置為 lazy initialize(即表 2 lazy="true"),組對象需要一個活躍的會話以查詢用戶。在定義 Group Users 之間關系時設置這個屬性為 lazy="false",則不需要第二個方法。在這種情況下,可能使用第一種方法 (findGroupByName) 列出組,用第二種方法(findPopulatedGroupByName)查看組細節(jié)。

             Spring IOC Hibernate

            使用 Spring 時,在 J2EE 容器內(nèi)和容器外工作一樣容易。比如在最近的項目中,我在 Eclipse 中,使用 HSQL 和本地數(shù)據(jù)庫對使用 Hibernate 事務管理器的 Hypersonic SQL 數(shù)據(jù)庫進行持久性單元測試。然后,在部署到 J2EE 服務器時,將持久層轉(zhuǎn)換為使用 J2EE 數(shù)據(jù)源(通過 JNDI)、JTA 事務和使用 FireBird (一個開放源代碼版本的 Interbase)。這是用 Spring 作為 IOC 容器完成的。

            從清單 3 中可以看出,Spring 允許加入依賴性。注意清單中應用程序上下文文件是如何配置 dataSource 的。dataSource 傳遞給 sessionFactorysessionFactory 傳遞給 UserDAO

            清單 3. Spring IOC Hibernate

          <beans>

          ?<!-- Datasource that works in any application server
          ??You could easily use J2EE data source instead if this were
          ??running inside of a J2EE container.
          ? -->
          ?<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
          ??<property name="driverClassName"><value>com.mysql.jdbc.Driver</value></property>
          ??<property name="url"><value>jdbc:mysql://localhost:3306/mysql</value></property>
          ??<property name="username"><value>root</value></property>
          ??<property name="password"><value></value></property>
          ?</bean>

          ?<!-- Hibernate SessionFactory -->
          ?<bean id="sessionFactory" class="org.springframework.orm.hibernate.LocalSessionFactoryBean">
          ??<property name="dataSource"><ref local="dataSource"/></property>
          ??
          ??<!-- Must references all OR mapping files. -->
          ??<property name="mappingResources">
          ???<list>
          ????????? <value>net/sf/hibernateExamples/User.hbm.xml</value>
          ????????? <value>net/sf/hibernateExamples/Group.hbm.xml</value>
          ????????? <value>net/sf/hibernateExamples/Role.hbm.xml</value>?????????
          ????????? <value>net/sf/hibernateExamples/ContactInfo.hbm.xml</value>
          ???</list>
          ??</property>
          ??
          ??<!-- Set the type of database; changing this one property will port this to Oracle,
          ??? MS SQL etc. -->
          ??<property name="hibernateProperties">
          ???<props>
          ???? <prop key="hibernate.dialect">net.sf.hibernate.dialect.MySQLDialect</prop>
          ???</props>
          ??</property>
          ?</bean>
          ?
          ?<!-- Pass the session factory to our UserDAO -->
          ?<bean id="userDAO" class="net.sf.hibernateExamples.UserDAO">
          ??<property name="sessionFactory"><ref local="sessionFactory"/></property>
          ?</bean>
          ?
          </beans>???

            設置了 UserDAO 后,下一步就是定義并使用更多的查詢以展示可以完成的操作。Hibernate 可以用預定義查詢將查詢存儲到源代碼之外,如清單 4 所示。

            清單 4. 預定義查詢
            [User.java]

          /**
          ?* @author Richard Hightower
          ?* ArcMind Inc. http://www.arc-mind.com
          ?* @hibernate.class table="TBL_USER" discriminator-value="2"
          ?* @hibernate.discriminator column="USER_TYPE"
          ?*
          ?* @hibernate.query name="AllUsers" query="from User user order by user.email asc"
          ?*
          ?* @hibernate.query name="OverheadStaff"
          ?* query="from Employee employee join employee.group g where g.name not in ('ENGINEERING','IT')"
          ?*
          ?* @hibernate.query name="CriticalStaff"
          ?* query="from Employee employee join employee.group g where g.name in ('ENGINEERING','IT')"
          ?*
          ?* @hibernate.query name="GetUsersInAGroup"
          ?* query="select user from Group g join g.users user"
          ?*
          ?* @hibernate.query name="GetUsersNotInAGroup"
          ?* query="select user from User user where user.group is null"
          ?*
          ?* @hibernate.query name="UsersBySalaryGreaterThan"
          ?* query="from User user inner join user.contactInfo info where info.salary > ?1"
          ?*
          ?* @hibernate.query name="UsersBySalaryBetween"
          ?* query="from User user join user.contactInfo info where info.salary between ?1 AND ?2"
          ?*
          ?* @hibernate.query name="UsersByLastNameLike"
          ?* query="from User user join user.contactInfo info where info.lastName like ?1"
          ?*
          ?* @hibernate.query name="GetEmailsOfUsers"
          ?* query="select user.email from Group g join g.users as user where g.name = ?1"
          ?*
          ?*/
          public class User {
          ???.
          ???.
          ???.???

            上述代碼定義了幾個預定義查詢。預定義查詢是存儲在 *.hbm.xml 文件中的查詢。在清單 5 中,可以看到如何執(zhí)行預定義查詢。

            清單 5. 使用預定義查詢
            [UserDAO.java]

          ?/**
          ? * Demonstrates a query that returns a String.
          ? */???????????????
          ?public String[] getUserEmailsInGroup(String groupName){
          ??List emailList =
          ??getHibernateTemplate().findByNamedQuery("GetEmailsOfUsers");
          ??return (String [])
          ???emailList.toArray(new String[emailList.size()]);
          ?}

          ?/**
          ? * Demonstrates a query that returns a list of Users
          ? *
          ? * @return A list of emails of all of the users in the authentication system.
          ? *
          ? */???????????????
          ?public List getUsers(){
          ??return getHibernateTemplate().findByNamedQuery("AllUsers");
          ?}

          ????? /**
          ?* Demonstrates passing a single argument to a query.
          ?*
          ?* @return A list of UserValue objects.
          ?*
          ?*/???????????????????
          ?public List getUsersBySalary(float salary){
          ??? return getHibernateTemplate()
          ??????? .findByNamedQuery("UsersBySalaryGreaterThan",
          ???????????? new Float(salary));
          ?}

          ?/**
          ? * Demonstrates passing multiple arguments to a query
          ? *
          ? * @return A list of UserValue objects.
          ? *
          ? */???????????????????
          ?public List getUsersBySalaryRange(float start, float stop){
          ??????????????? return getHibernateTemplate()
          ?? .findByNamedQuery("UsersBySalaryBetween",
          ???new Object[] {new Float(start), new Float(stop)});
          ?}

            查詢進行時,可以在持久層中加上最后一層:使用 Spring 的事務管理。

             Spring 管理事務

            Spring 可以聲明式地管理事務。例如,UserDAO.addUser 方法當前不是在單個事務中執(zhí)行的。因此,組中的每一個用戶都插入到自己的事務中,如清單 6 所示。

            清單 6. 添加一組用戶
            [UserDAO.java]

          /**
          ?* @param group
          ?*/
          public void addGroup(Group group) {
          ?getHibernateTemplate().save(group);
          ?
          }

          [UserDAOTest.java]

          public void testAddGroupOfUsers(){
          ?Group group = new Group();
          ?
          ?for (int index=0; index < 10; index++){
          ??User user = new User();
          ??user.setEmail("rick"+index+"@foobar.com" );
          ??user.setPassword("foobar");
          ??group.addUser(user);?
          ?}
          ?
          ?group.setName("testGroup");
          ?
          ?userDAO.addGroup(group);
          ?assertNotNull(group.getId());
          ?
          ?Group group2 = userDAO.findPopulatedGroupByName("testGroup");
          ?
          ?assertEquals("testGroup",group2.getName());
          ?assertEquals(10, group2.getUsers().size());
          ?String email = ((User)group2.getUsers().get(0)).getEmail();
          ?assertEquals("rick0@foobar.com", email);

          }

            不建議使用上述解決方案,因為每一個 User 都要在自己的事務中插入到數(shù)據(jù)庫中。如果出現(xiàn)問題,那么只能添加部分用戶。如果希望保留 ACID 屬性(即保證所有都發(fā)生或者所有都不發(fā)生),可以通過程序進行事務管理,但是它很快就會變得一團糟了。相反,應使用 Spring AOP 來支持聲明式的事務,如清單 7 所示。

            清單 7. 聲明式管理事務
            [applicationContext.xml]

          <!-- Pass the session factory to our UserDAO -->
          ?? <bean id="userDAOTarget" class="net.sf.hibernateExamples.UserDAOImpl">
          ?<property name="sessionFactory"><ref local="sessionFactory"/></property>
          ?? </bean>
          ?
          ?? <bean id="transactionManager"
          ?? ?class="org.springframework.orm.hibernate.HibernateTransactionManager">
          ????? <property name="sessionFactory"><ref bean="sessionFactory"/></property>
          ?? </bean>

          <bean id="userDAO"
          ???? class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
          ?<property name="transactionManager"><ref local="transactionManager"/></property>
          ?<property name="target"><ref local="userDAOTarget"/></property>
          ?<property name="transactionAttributes">
          ??<props>
          ???<prop key="addGroup">PROPAGATION_REQUIRED</prop>
          ??</props>
          ?</property>
          </bean>

            注意在準備清單 7 的代碼時,我重新改寫了 UserDAO 并提取了其接口。這個接口現(xiàn)在是 UserDAO,它的實現(xiàn)類是 UserDAOImpl。這樣清單 7 中的事務代碼就使用了帶有事務屬性 (PROPAGATION_REQUIRED) UserDAO.addGroup() 方法。現(xiàn)在只要底層數(shù)據(jù)庫支持,就可以在一個事務中添加所有用戶。

             結(jié)束語

            在本文中,介紹了如何使用 Hibernate Spring 實現(xiàn)一個事務持久層。Hibernate 是一種先進的 OR 映射工具,而 Spring 是一個 AOP 框架和 IOC 容器。這兩種技術的綜合使用,使得開發(fā)人員可以編寫媲美數(shù)據(jù)庫廠商的代碼,它可以在 J2EE 容器中運行,也可以單獨運行。使用了 DbUnit JUnit 的擴展)構(gòu)建和測試本文中例子的所有代碼,雖然這不是討論的重點。

          posted on 2006-04-25 20:21 白切面片 閱讀(344) 評論(1)  編輯  收藏

          評論:
          # re: 用 Hibernate 和 Spring 開發(fā)事務持久層 2007-08-11 20:30 | dreamstone
          寫的不錯  回復  更多評論
            

          只有注冊用戶登錄后才能發(fā)表評論。


          網(wǎng)站導航:
           
          主站蜘蛛池模板: 含山县| 凤城市| 将乐县| 扶沟县| 呼和浩特市| 黑水县| 若尔盖县| 阳西县| 定陶县| 赤壁市| 金溪县| 乳山市| 郎溪县| 磐石市| 阜新市| 仁化县| 合肥市| 乐东| 博白县| 仪征市| 青铜峡市| 遂平县| 和硕县| 庆元县| 胶州市| 定安县| 手游| 凤阳县| 江源县| 信宜市| 罗田县| 闽清县| 临安市| 当涂县| 绥棱县| 南和县| 稷山县| 尼木县| 安康市| 孝昌县| 望都县|