1. 程式人生 > >事務: spring+hibernate實現事務回滾及其他

事務: spring+hibernate實現事務回滾及其他

程式碼控制的事務管理
2. 引數化配置的事務管理
下面就這兩種方式進行介紹。
u 程式碼控制的事務管理
首先,進行以下配置,假設配置檔案為(Application-Context.xml):
<beans>
<bean id="dataSource"
class="org.apache.commons.dbcp.BasicDataSource"

destroy-method="close">
<property name="driverClassName">
<value>net.sourceforge.jtds.jdbc.Driver</value>
</property>

<property name="url">
<value>jdbc:jtds:sqlserver://127.0.0.1:1433/Sample</value>
</property>
<property name="username">
<value>test</value>
</property>
<property name="password">
<value>changeit</value>
</property>
</bean>
<bean id="transactionManager"

class="org.springframework.jdbc.datasource.DataSourceTransac
tionManager">
<property name="dataSource">
<ref local="dataSource" />
</property>
</bean>
<bean id="userDAO" class="net.xiaxin.dao.UserDAO">
<property name="dataSource">
<ref local="dataSource" />
</property>

<property name="transactionManager">
<ref local="transactionManager" />
</property>
</bean>
</beans>
配置中包含了三個節點:
Ø dataSource
這裡我們採用了apache dhcp元件提供的DataSource實現,併為其配置了JDBC驅動、資料庫URL、使用者名稱和密碼等引數。
Ø transactionManager
針對JDBC DataSource型別的資料來源,我們選用了DataSourceTransactionManager作為事務管理元件。
如果需要使用基於容器的資料來源(JNDI),我們可以採用如下配置:<bean id="dataSource"
class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName">
<value>jdbc/sample</value>
</property>
</bean>
<bean id="transactionManager"
class="org.springframework.transaction.jta.JtaTransactionManager"
/>
Ø userDAO
申明瞭一個UserDAO Bean,併為其指定了dataSource和transactionManger資源。
UserDAO對應的程式碼如下:
public class UserDAO {
private DataSource dataSource;
private PlatformTransactionManager transactionManager;

public PlatformTransactionManager getTransactionManager() {
return transactionManager;
}
public void setTransactionManager(PlatformTransactionManager
transactionManager) {
this.transactionManager = transactionManager;
}
public DataSource executeTestSource() {
return dataSource;
}
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
}
public void insertUser() {
TransactionTemplate tt =
new TransactionTemplate(getTransactionManager());
tt.execute(new TransactionCallback() {
public Object doInTransaction(TransactionStatus status) {
JdbcTemplate jt = new JdbcTemplate(executeTestSource());
jt.update(
"insert into users (username) values ('xiaxin');");
jt.update(
"insert into users (id,username) values(2,
'erica');");
return null;
}
});

}
}
可以看到,在insertUser方法中,我們引入了一個新的模板類:
org.springframework.transaction.support.TransactionTemplate。
TransactionTemplate封裝了事務管理的功能,包括異常時的事務回滾,以及操作成功後的事務提交。和JdbcTemplate一樣,它使得我們無需在瑣碎的try/catch/finally程式碼中徘徊。
在doInTransaction中進行的操作,如果丟擲未捕獲異常將被自動回滾,如果成功執行, 則將被自動提交。
這裡我們故意製造了一些異常來觀察資料庫操作是否回滾(通過在第二條語句中更新自增ID欄位故意觸發一個異常):
編寫一個簡單的TestCase來觀察實際效果:
InputStream is = new FileInputStream("Application-Context.xml");
XmlBeanFactory factory = new XmlBeanFactory(is);
UserDAO userDAO = (UserDAO) factory.getBean("userDAO");
userDAO.insertUser();
相信大家多少覺得上面的程式碼有點凌亂,Callback類的編寫似乎也有悖於日常的程式設計習慣(雖然筆者覺得這一方法比較有趣,因為它巧妙的解決了筆者在早期自行開發資料訪問模板中曾經遇到的問題)。如何進一步避免上面這些問題?Spring 的容器事務管理機制在這裡即體現出其強大的能量。u 引數化配置的事務管理在上面的Application-Context.xml增加一個事務代理(UserDAOProxy)配置,同時,由於事務由容器管理,UserDAO不再需要TransactionManager設定,將其移除:
<bean id="UserDAOProxy"
class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
<property name="transactionManager">
<ref bean="transactionManager" />
</property>
<property name="target">
<ref local="userDAO" />
</property>
<property name="transactionAttributes">
<props>
<prop key="insert*">PROPAGATION_REQUIRED</prop>
<prop key="*">PROPAGATION_REQUIRED,readOnly</prop>

</props>
</property>
</bean>
<bean id="userDAO" class="net.xiaxin.dao.UserDAO">
<property name="dataSource">
<ref local="dataSource" />
</property>
</bean>

上面的配置中,UserDAOProxy節點配置了一個針對userDAO bean的事務代理(由target屬性指定)。通過transactionAttributes屬性,我們指定了事務的管理策略,即對所有以insert開頭的方法進行事務管理,如果被管理方法丟擲異常,則自動回滾方法中的事務,如果成功執行,則在方法完成之後進行事務提交。另一方面對於其他方法(通過萬用字元*表示),則進行只讀事務管理,以獲得更好的效能。與之對應UserDAO.insertUser的程式碼修改如下:

public void insertUser(RegisterInfo regInfo) {
JdbcTemplate jt = new JdbcTemplate(executeTestSource());
jt.update("insert into users (username) values ('xiaxin');");
jt.update("insert into users (id,username) values (2,'erica');");
}
測試程式碼修改如下:
InputStream is = new FileInputStream("Application-Context.xml");
XmlBeanFactory factory = new XmlBeanFactory(is);
//注意這裡須通過代理Bean"userDAOProxy"獲得引用,而不是直接getBean(“userDAO”)
//此外這裡還存在一個有關強制轉型的潛在問題,請參見Hibernate in Spring一節後
//關於強制轉型的補充描述。
UserDAO userDAO = (UserDAO) factory.getBean("userDAOProxy");
userDAO.insertUser();
可以看到,insertUser變得非常簡潔。資料邏輯清晰可見,對比前面程式碼控制的事務管理,以及傳統的JDBC操作,相信大家會有一些霍然開朗的感覺。細心的讀者會說,這只不過將程式碼轉移到了配置檔案,並沒有減少太多的工作量。這點區別也許並不重要,從應用維護的角度而言,配置化的事務管理顯然更具優勢。何況,實際開發中,如果前期設計細緻,方法的事務特性確定之後一般不會發生大的變動,之後頻繁的維護過程中,我們只需面對程式碼中的資料邏輯即可。上面我們結合JdbcTemplate介紹了Spring中的模板操作以及事務理機制。Spring作為一個開放式的應用開發平臺。同時也針對其他元件提供了良好的支援。在持久層,Spring提供面向了Hibernate、ibatis和JDO的模板實現,同樣,這些實現也為我們的開發提供了強有力的支援。
下面我們就hibernate、ibatis這兩種主流持久層框架在Spring中的使用進行介紹。至於JDO,由於實際開發中使用並不廣泛(實際上筆者覺得JDO前景堪憂),這裡也就不重點介紹,有興趣的讀者可參見Spring-Reference中的相關章節。

Hibernate in Spring

Hibernate在開源的持久層框架中無疑是近期最為鮮亮的角色,其作者甚至被邀請加入
新版EJB設計工作之中,足見Hibernate設計的精彩貼切。關於Hibernate的使用,在筆者
的另外一篇文件中進行了探討:
《Hibernate開發指南》