在 Spring 系列的第一部分中,我們介紹了 Spring 框架的七大模組,重點講解了控制反轉(IoC)容器與面向切面編程(AOP)的核心概念。本文將接續前文,透過一個銀行帳戶開戶的實際案例,深入示範如何將 Hibernate 的持久化能力與 Spring AOP 的宣告式事務管理相結合,打造一個健壯、可維護的資料存取層。
企業級應用中,日誌、事務、安全等系統級服務往往散落在各個業務組件中,造成所謂的「橫切關注點」。Spring AOP 允許我們將這些通用功能模組化為「切面」,再以宣告式方式織入目標物件,無需修改業務代碼即可實現事務邊界的自動管理。TransactionProxyFactoryBean 便是 Spring 提供的便利代理類,它能攔截方法調用並自動套用事務上下文。
Hibernate 是 Java 平台上功能完備的開源 ORM 框架,支援關聯、繼承、多態等物件導向特性,並提供 HQL(Hibernate Query Language)作為面向物件的查詢語言。Spring 對 Hibernate 提供一級支援,整合了 IoC 容器的便利特性,解決了傳統整合中的常見問題,並納入 Spring 統一的事務與 DAO 異常層級體系。
系統包含 Customer(客戶)與 Account(帳戶)兩個實體,呈現一對多關聯。資料庫採用 Cloudscape(Derby),透過 Hibernate 映射檔(.hbm.xml)定義物件關聯關係。
在應用程式上下文中,我們將 JDBC DataSource 與 Hibernate SessionFactory 定義為 Bean,並註冊映射檔:
<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>
<bean id="exampleHibernateProperties" class="org.springframework.beans.factory.config.PropertiesFactoryBean">
<property name="properties">
<props>
<prop key="hibernate.dialect">net.sf.hibernate.dialect.DerbyDialect</prop>
<prop key="hibernate.show_sql">false</prop>
<prop key="hibernate.c3p0.minPoolSize">5</prop>
<prop key="hibernate.c3p0.maxPoolSize">20</prop>
</props>
</property>
</bean>
<bean id="exampleSessionFactory" class="org.springframework.orm.hibernate.LocalSessionFactoryBean">
<property name="dataSource"><ref local="exampleDataSource"/></property>
<property name="hibernateProperties"><ref bean="exampleHibernateProperties"/></property>
<property name="mappingResources">
<list>
<value>Customer.hbm.xml</value>
<value>Account.hbm.xml</value>
</list>
</property>
</bean>
CustomerDAOImpl 繼承 HibernateDaoSupport,利用 getHibernateTemplate() 簡化 Session 管理:
public class CustomerDAOImpl extends HibernateDaoSupport implements CustomerDAO {
public void addCustomer(Customer customer) {
getHibernateTemplate().save(customer);
}
public Customer getCustomerAccountInfo(Customer customer) {
List list = getHibernateTemplate().find(
"from Customer customer where customer.userId = ?",
customer.getUserId(), Hibernate.STRING);
return (list.size() > 0) ? (Customer) list.get(0) : null;
}
}
透過 TransactionProxyFactoryBean 將事務屬性套用至 DAO 方法:
<bean id="customerDAOTarget" class="springexample.hibernate.CustomerDAOImpl">
<property name="sessionFactory"><ref bean="exampleSessionFactory"/></property>
</bean>
<bean id="customerDAO" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
<property name="transactionManager"><ref bean="transactionManager"/></property>
<property name="target"><ref local="customerDAOTarget"/></property>
<property name="transactionAttributes">
<props>
<prop key="addCustomer">PROPAGATION_REQUIRED</prop>
<prop key="getCustomerAccountInfo">PROPAGATION_REQUIRED,readOnly</prop>
</props>
</property>
</bean>
PROPAGATION_REQUIRED 相當於 EJB 的 TX_REQUIRED:若事務已存在則加入,否則啟動新事務。
下載 Spring 框架、Apache Ant 與 Cloudscape,配置 build.xml 路徑後執行 build 指令。客戶端程式 CreateBankCustomerClient 會建立客戶與帳戶物件,調用 DAO 完成持久化,最後查詢驗證資料是否正確寫入資料庫。
本文展示了 Spring AOP 與 Hibernate 的無縫整合:Spring 管理資源與事務,Hibernate 負責 ORM 映射,兩者結合消除了樣板式事務代碼,實現了乾淨、可測試的持久層架構。下一篇將介紹 Spring MVC 模組,簡化 Web 應用開發。