作者:Naveen Balani 來(lái)自:IBM
在這個(gè)系列的
前一期中,我介紹了
Spring 框架的 7 個(gè)模塊,包括 Spring AOP 和控制反轉(zhuǎn)(IOC)容器。然后我用一個(gè)簡(jiǎn)單的示例演示了 IOC 模式(由 Spring IOC 容器實(shí)現(xiàn))如何用松散耦合的方式集成分散的系統(tǒng)。
現(xiàn)在,我從我上次結(jié)束的地方開(kāi)始,采用與上次類似的示例,演示 Spring AOP 和 Spring Hibernate 持久性支持的聲明性事務(wù)處理,所以我首先從對(duì)這兩項(xiàng)技術(shù)的深入研究開(kāi)始。
Spring AOP
軟件系統(tǒng)通常由多個(gè)組件構(gòu)成,每個(gè)組件負(fù)責(zé)一個(gè)特定的功能領(lǐng)域。但是,這些組件也經(jīng)常承擔(dān)它們的核心功能之外的額外責(zé)任。系統(tǒng)服務(wù)(例如日志、事務(wù)管理和安全性)經(jīng)常發(fā)現(xiàn)自己跑到了別的組件的領(lǐng)域里,而這些組件的核心職責(zé)是其他事情。結(jié)果就是所謂的“代碼糾纏”,或者更簡(jiǎn)單點(diǎn)兒說(shuō)“一團(tuán)糟”。面向方面編程是一種試圖解決這個(gè)問(wèn)題的編程技術(shù),它把關(guān)注點(diǎn)的隔離提升為核心的編程概念。
使用 AOP 時(shí),仍然是在一個(gè)地方定義系統(tǒng)的公共功能,但是可以聲明性地定義 如何 和 在哪里 應(yīng)用這個(gè)功能。如果對(duì)橫切關(guān)注點(diǎn)(例如日志和事務(wù)管理)進(jìn)行了模塊化,那么不用修改每個(gè)單獨(dú)的類,就可以向代碼中添加新特性。這類模塊化的關(guān)注點(diǎn)稱作 方面。
以一個(gè)企業(yè)應(yīng)用程序?yàn)槔?。這類應(yīng)用程序通常要求類似于安全性和事務(wù)支持的服務(wù)。顯然,可以把這些服務(wù)的支持直接編寫到要求服務(wù)的每個(gè)類當(dāng)中,但是更希望能夠不必為大量事務(wù)性上下文編寫同樣的事務(wù)處理代碼。如果使用 Spring AOP 進(jìn)行事務(wù)處理,那么可以聲明性地安排適當(dāng)?shù)姆椒ㄕ{(diào)用,而不必逐個(gè)安排。
Spring AOP 提供了幾個(gè)方面,可以為 JavaBean 聲明事務(wù)。例如,TransactionProxyFactoryBean 是個(gè)方便的代理類,能夠攔截對(duì)現(xiàn)有類的方法調(diào)用,并把事務(wù)上下文應(yīng)用到事務(wù) bean。在下面的示例中會(huì)看到這個(gè)類的實(shí)際應(yīng)用。
Hibernate
Spring 框架提供了對(duì)
Hibernate、JDO 和 iBATIS SQL Maps 的集成支持。Spring 對(duì) Hibernate 的支持是第一級(jí)的,整合了許多 IOC 的方便特性,解決了許多典型的 Hibernate 集成問(wèn)題。框架對(duì) Hibernate 的支持符合 Spring 通用的事務(wù)和數(shù)據(jù)訪問(wèn)對(duì)象(DAO)異常層次結(jié)構(gòu)。
Spring 為使用選擇的 OR 映射層來(lái)創(chuàng)建數(shù)據(jù)訪問(wèn)應(yīng)用程序提供了支持。因?yàn)樗袞|西都設(shè)計(jì)成一組可重用 JavaBean,所以不管選擇什么技術(shù),都能以庫(kù)的格式訪問(wèn)大多數(shù) Spring 的 OR 映射支持。 ApplicationContext 或 BeanFactory 內(nèi)部的 OR 映射的好處是簡(jiǎn)化了配置和部署。
Hibernate 是 Java 平臺(tái)上一個(gè)功能全面的、開(kāi)源的 OR 映射框架。Hibernate 支持開(kāi)發(fā)符合常規(guī) Java 理念的持久性類 —— 包括關(guān)聯(lián)、繼承、多態(tài)、復(fù)合以及 Java 集合框架。Hibernate 查詢語(yǔ)言(HQL)被設(shè)計(jì)成 SQL 的一個(gè)微型面向?qū)ο髷U(kuò)展,它是對(duì)象和關(guān)系世界之間的橋梁。Hibernate 也支持用原始 SQL 或基于 Java 的標(biāo)準(zhǔn)和示例查詢表達(dá)查詢。Hibernate 使用 XML(*.hbm.xml) 文件把 Java 類映射到表,把 JavaBean 屬性映射到數(shù)據(jù)庫(kù)表。
通過(guò)
JDBC 技術(shù),支持所有的 SQL 數(shù)據(jù)庫(kù)管理系統(tǒng)。Hibernate 與所有流行的 J2EE 應(yīng)用程序服務(wù)器和 Web 容器都很好地集成。
實(shí)際示例
一個(gè)銀行應(yīng)用程序示例可以讓您自己看到 Spring AOP 和 Hibernate 一起工作有多么好。銀行帳戶用例允許用戶 (Customer) 在一個(gè)事務(wù)中打開(kāi)一個(gè)或多個(gè)銀行帳戶。用戶可以申請(qǐng)多個(gè)銀行帳戶,可以選擇是支票帳戶類型或者是儲(chǔ)蓄帳戶類型。
應(yīng)用程序數(shù)據(jù)庫(kù)(Cloudscape?)容納所有客戶和帳戶信息。在這個(gè)例子中,假設(shè)在 Customer 和 Account 類之間存在 1:N 的關(guān)聯(lián)。在實(shí)際生活場(chǎng)景中,關(guān)聯(lián)可能需要按 m:n 建模,才能支持聯(lián)合帳戶。
由于用戶必須可以在一個(gè)事務(wù)中申請(qǐng)多個(gè)帳戶,所以首先要為數(shù)據(jù)庫(kù)交互實(shí)現(xiàn)一個(gè) DOA 模式。然后要設(shè)置 Spring AOP 的 TransactionProxyFactoryBean,讓它攔截方法調(diào)用并聲明性地把事務(wù)上下文應(yīng)用到 DOA。
Hibernate 實(shí)踐
在 Spring 框架中,像 JDBC DataSource 或 Hibernate SessionFactory 這樣的資源,在應(yīng)用程序上下文中可以用 bean 實(shí)現(xiàn)。需要訪問(wèn)資源的應(yīng)用程序?qū)ο笾恍柰ㄟ^(guò) bean 引用得到這類預(yù)先定義好的實(shí)例的引用即可(這方面的更多內(nèi)容在 下一節(jié)中)。在清單 1 中,可以看到示例銀行應(yīng)用程序的一段摘錄:XML 應(yīng)用程序上下文定義顯示了如何設(shè)置 JDBC DataSource,并在上面放一個(gè) Hibernate SessionFactory。
清單 1. JDBC DataSource 和 HibernateSessionFactory 連接
<!-- DataSource Property -->
<bean id="exampleDataSource"
class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName">
<value>org.apache.derby.jdbc.EmbeddedDriver</value>
</property>
<property name="url">
<value>jdbc:derby:springexample;create=true</value>
</property>
</bean>
<!-- Database Property -->
<bean id="exampleHibernateProperties"
class="org.springframework.beans.factory.config.PropertiesFactoryBean">
<property name="properties">
<props>
<prop key="hibernate.hbm2ddl.auto">update</prop>
<prop
key="hibernate.dialect">net.sf.hibernate.dialect.DerbyDialect</prop>
<prop
key="hibernate.query.substitutions">true ‘T‘, false ‘F‘</prop>
<prop key="hibernate.show_sql">false</prop>
<prop key="hibernate.c3p0.minPoolSize">5</prop>
<prop key="hibernate.c3p0.maxPoolSize">20</prop>
<prop key="hibernate.c3p0.timeout">600</prop>
<prop key="hibernate.c3p0.max_statement">50</prop>
<prop
key="hibernate.c3p0.testConnectionOnCheckout">false</prop>
</props>
</property>
</bean>
<!-- Hibernate SessionFactory -->
<bean id="exampleSessionFactory"
class="org.springframework.orm.hibernate.LocalSessionFactoryBean">
<property name="dataSource">
<ref local="exampleDataSource"/>
</property>
<property name="hibernateProperties">
<ref bean="exampleHibernateProperties" />
</property>
<!-- OR mapping files. -->
<property name="mappingResources">
<list>
<value>Customer.hbm.xml</value>
<value>Account.hbm.xml</value>
</list>
</property>
</bean>
清單 1 顯示了如何為示例應(yīng)用程序數(shù)據(jù)庫(kù)(是 Cloudscape)配置數(shù)據(jù)源 bean (exampleDataSource)。exampleDatasource 被連接到 Spring Hibernate 的 SessionFactory。請(qǐng)注意 *.hbm.xml 指定了示例應(yīng)用程序的 OR 映射文件。
數(shù)據(jù)源和會(huì)話工廠設(shè)置好之后,下一步就是在 DAO 中連接,在 CustomerDAOImpl 示例中,要使用 SessionFactory。接下來(lái),插入 Spring 的 TransactionProxyFactoryBean,它會(huì)攔截對(duì)應(yīng)用程序的 CustomerDAOImpl 對(duì)象的方法調(diào)用,并聲明性地在它上面應(yīng)用事務(wù)。
清單 2. 將應(yīng)用程序 DAO 和 TransactionManager 編寫在一起
<!-- Pass the session factory to our CustomerDAO -->
<bean id="customerDAOTarget"
class="springexample.hibernate.CustomerDAOImpl">
<property name="sessionFactory"><ref local="exampleSessionFactory"/>
</property>
</bean>
<bean id="transactionManager"
class="org.springframework.orm.hibernate.HibernateTransactionManager">
<property name="sessionFactory">
<ref bean="exampleSessionFactory"/>
</property>
</bean>
<bean id="userDAO"
class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
<property name="transactionManager"><ref local="transactionManager"/>
</property>
<property name="target"><ref local="customerDAOTarget"/>
</property>
<property name="transactionAttributes">
<props>
<prop key="addCustomer">PROPAGATION_REQUIRED</prop>
</props>
</property>
</bean>
在 清單 2 的這個(gè)示例中,CustomerDAOImpl 類的 addCustomer 方法是作為事務(wù)的一部分執(zhí)行的,有一個(gè)事務(wù)屬性 PROPAGATION_REQUIRED。這個(gè)屬性等價(jià)于 EJB 容器的 TX_REQUIRED。如果想讓這個(gè)方法一直在事務(wù)中運(yùn)行,可以使用 PROPAGATION_REQUIRED。如果事務(wù)已經(jīng)在運(yùn)行,那么 bean 方法會(huì)加入事務(wù),否則 Spring 的輕量級(jí)事務(wù)管理器會(huì)啟動(dòng)一個(gè)事務(wù)。如果想在調(diào)用組件服務(wù)時(shí)總是啟動(dòng)新事務(wù),可以使用 PROPAGATION_REQUIRES_NEW 屬性。
應(yīng)用程序的連接完成之后,現(xiàn)在來(lái)進(jìn)一步查看源代碼。
分析這個(gè)!
如果以前沒(méi)這么做過(guò),那么請(qǐng)
下載這篇文章的源代碼。把源 zip 文件釋放到計(jì)算機(jī)中的任何位置上,例如 c:\。會(huì)創(chuàng)建一個(gè)叫作 SpringProjectPart2 的文件夾。src\spring 文件夾包含示例應(yīng)用程序的 Hibernate 映射文件和 Spring 配置文件。src\springexample\hibernate 文件包含應(yīng)用程序的源代碼。
在這里會(huì)發(fā)現(xiàn)兩個(gè)類,即 Customer 和 Account,它們用 Hibernate 映射文件映射到兩個(gè)表。Customer 類代表客戶信息,Account 代表客戶的帳戶信息。正如前面提到的,我把這兩個(gè)類按照 1: N 關(guān)系進(jìn)行建模,即一個(gè) Customer 可以擁有多個(gè) Account。清單 3 顯示了 Customer 對(duì)象的 Hibernate 映射文件。
清單 3. Customer 對(duì)象的 Hibernate 映射文件
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 2.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd">
<hibernate-mapping>
<class
name="springexample.hibernate.Customer"
table="TBL_CUSTOMER"
dynamic-update="false"
dynamic-insert="false">
<id
name="id"
column="CUSTOMER_ID"
type="java.lang.Long"
unsaved-value="-1"
>
<generator class="native">
</generator>
</id>
<set name ="accounts"
inverse = "true"
cascade="all-delete-orphan">
<key column ="CUSTOMER_ID"/>
<one-to-many class="springexample.hibernate.Account"/>
</set>
<property
name="email"
type="string"
update="false"
insert="true"
column="CUSTOMER_EMAIL"
length="82"
not-null="true"
/>
<property
name="password"
type="string"
update="false"
insert="true"
column="CUSTOMER_PASSWORD"
length="10"
not-null="true"
/>
<property
name="userId"
type="string"
update="false"
insert="true"
column="CUSTOMER_USERID"
length="12"
not-null="true"
unique="true"
/>
<property
name="firstName"
type="string"
update="false"
insert="true"
column="CUSTOMER_FIRSTNAME"
length="25"
not-null="true"
/>
<property
name="lastName"
type="string"
update="false"
insert="true"
column="CUSTOMER_LASTTNAME"
length="25"
not-null="true"
/>
</class>
</hibernate-mapping>
set name="accounts" 和一對(duì)多類標(biāo)簽指定了 Customer 和 Account 之間的關(guān)系。我還在 Account.hbm.xml 文件中定義了 Account 對(duì)象的映射。
CustomerDAOImpl.java 代表應(yīng)用程序的 DAO,它在應(yīng)用程序數(shù)據(jù)庫(kù)中插入客戶和帳戶信息。CustomerDAOImpl 擴(kuò)展了 Spring 的 HibernateDaoSupport,它用 Spring HibernateTemplate 簡(jiǎn)化了會(huì)話管理。這樣,可以通過(guò) getHibernateTemplate() 方法保存或檢索數(shù)據(jù)。下面顯示的 getCustomerAccountInfo() 對(duì) Customer 進(jìn)行 查找,通過(guò) getHibernateTemplate().find 方法用 HQL 得到客戶的帳戶信息,如清單 4 所示。
清單 4. DAO 實(shí)現(xiàn)
public class CustomerDAOImpl extends HibernateDaoSupport
implements CustomerDAO{
public void addCustomer(Customer customer) {
getHibernateTemplate().save(customer);
// TODO Auto-generated method stub
}
public Customer getCustomerAccountInfo(Customer customer) {
Customer cust = null;
List list = getHibernateTemplate().find("from Customer customer " +
"where customer.userId = ?" ,
customer.getUserId(),Hibernate.STRING);
if(list.size() > 0){
cust = (Customer) list.get(0);
}
return cust;
}
所有這些都應(yīng)當(dāng)很容易掌握?,F(xiàn)在來(lái)看代碼的實(shí)際應(yīng)用!
運(yùn)行應(yīng)用程序
要運(yùn)行示例應(yīng)用程序,必須首先
下載 Spring 框架 和它的全部依賴文件。接下來(lái),釋放框架到某一位置(比如 c:\ ),這會(huì)創(chuàng)建文件夾 C:\spring-framework-1.2-rc2(針對(duì)當(dāng)前發(fā)行版)。在繼續(xù)之前還必須下載和釋放
Apache Ant 和
Cloudscape。下載 Cloudscape 之后,把它釋放到 c:\ ,這會(huì)創(chuàng)建文件夾 C:\Cloudscape_10.0。
接下來(lái),釋放源代碼到 c:\ ,這會(huì)創(chuàng)建 SpringProject2 文件夾。接下來(lái)修改 build.xml 文件的入口,用實(shí)際安裝 Spring 的位置代替 C:\spring-framework-1.2-rc2,用實(shí)際安裝 Cloudscape 的位置代替 C:\Program Files\IBM\Cloudscape_10.0。
打開(kāi)命令行提示符,進(jìn)入 SpringProject 目錄,在命令行提示符下輸入以下命令:build.
這會(huì)構(gòu)建并運(yùn)行 CreateBankCustomerClient 類,它會(huì)創(chuàng)建 Customer 類對(duì)象,用數(shù)據(jù)填充它,創(chuàng)建 Account 對(duì)象,填充它,并把它添加到 Customer 對(duì)象。
然后 CreateBankCustomerClient 會(huì)調(diào)用 CustomerDAOImpl.addCustomer 類,添加客戶和帳戶信息。一旦插入完成,CreateBankCustomerClient 會(huì)調(diào)用 CustomerDAOImpl.getCustomerAccountInfo 方法,根據(jù) userid 得到客戶和帳戶信息。如果 CreateBankCustomerClient 執(zhí)行成功,會(huì)在控制臺(tái)上看到打印出 userid。也可以查詢 Cloudscape 數(shù)據(jù)庫(kù)檢索客戶和帳戶信息。
結(jié)束語(yǔ)
在三部分的 Spring 系列 的第 2 部分中,我介紹了如何集成 Spring Hibernate 和 Spring AOP。結(jié)果是一個(gè)強(qiáng)健的持久性框架,支持聲明性的實(shí)現(xiàn)事務(wù)。
在這個(gè)系列的下一篇,也是最后一篇文章中,我將介紹 Spring 的 MVC 模塊,介紹如何用它來(lái)簡(jiǎn)化基于 Web 的應(yīng)用程序的創(chuàng)建。