Spring事務的學習
??????
今天對
spring
的
AOP
事務有了一個新的認識,所以趕緊把今天的學習記下來,希望在今后的學習中能夠起到一些作用,也能對今天的認識做一次總結。
1?????????
同事的
spring
分享
先看一段代碼:
???
Connection conn = Conn.getConnection();
??? conn.setAutoCommit(false);
??? ……..
??? ……...
??? conn.rollback();
??? conn.commit();
???
數據庫的事務是針對
Connection
的。
???
接著再看一段代碼:(
spring
中事務的一段學習代碼,這段代碼是把
spring
和
hibernate
結合在一起的,增加了理解上的難度,因為我的出發點一開始不要
hibernate
,就光用
jdbc
來進行數據庫事務,但是沒有其他好的代碼,就這樣吧)
??? public Long addLineItem(Long orderId, LineItem lineItem){
?????? log("OrderListDAOHibernate.addLineItem : Start...");
?????? OrderList orderList = (OrderList) getHibernateTemplate().load(OrderList.class, orderId);
?????? lineItem.setOrderList(orderList);
?????? getHibernateTemplate().saveOrUpdate(lineItem);
?????? getHibernateTemplate().saveOrUpdate(orderList);
?????? log("OrderListDAOHibernate.addLineItem : Ending...");
?????? return lineItem.getId();
??? }
???
在這個代碼的配置文件中,把
addLineItem
做為一個切入點,進行事務,也就是說,在
addLineItem
的外面,再包上一層事務的外殼。
???
但是這個時候,問題出來了,事務是針對
Connection
的,而上面的兩個連續的
HibernateTemplate
執行的
saveOrUpdate
中的
Connection
必須是一致才能用事務,
spring
怎么做到這一點的呢?(這個問題也就是在找
spring
的事務例子前,我想的
spring
中用
jdbc
來進行事務,怎么樣讓
Connection
保持一致呢?但是沒有
jdbc
的例子,只有整合
hibernate
或者
ibatis
的例子,但是,我想,原理是一樣的吧。)
???
解決問題的思路:
HibernateTemplate
中的
Connection
必定一致。那么就從
HibernateTemplate
入手。
???
看
spring
的源代碼,既然是
Hibernate
,那么,就沒有
Connection
給你看,只有
Session
,由
Session
來管理
Connection
,那么用事務來控制的話,這個
Session
必定在所有該事務中是一致的。于是在
HibernateTemplate
中找到:
protected Session getSession() {
?????? if (isAlwaysUseNewSession()) {
return SessionFactoryUtils.getNewSession(getSessionFactory(), getEntityInterceptor());
?????? }
?????? else if (!isAllowCreate()) {
return SessionFactoryUtils.getSession(getSessionFactory(), false);
?????? }
?????? else {
return SessionFactoryUtils.getSession(
????????????????? getSessionFactory(), getEntityInterceptor(), getJdbcExceptionTranslator());
?????? }
??? }
看來在
SessionFactoryUtils
里面,接著在
SessionFactoryUtils.getSession
中找:
這個方法太長了,太復雜了,從簡,發現了非常關鍵的一點:
SessionHolder sessionHolder = (SessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);
假如
sessionHolder
不等于空,說明,在事務中有這樣一個還沒有
commit
的
session
,那么就返回這個
session
,假如等于空,新建一個
session
,并且在事務里加入這個
session
。這段代碼的意思大概是這樣,太繁雜了,只能猜,也肯定是如此。
再看
getHibernateTemplate()
方法來自繼承
HibernateDaoSupport
,看了電子書《
spring-reference
》的第九章“
Dao
支持”,
Dao
的支持類可以有好多,如:
JdbcDaoSupport
,
HibernateDaoSupport
,
JdoDaoSupport
等等。
既然前面一開始就是從
jdbc
的
spring
事務控制引起的,那么看到了同樣的
HibernateDaoSupport---JdbcDaoSupport
,那么
JdbcDaoSupport
也應該有
getJdbcTemplate()
這個方法,并且返回
JdbcTemplate
這個類。
果然如此。
于是剖析
JdbcTemplate
是不是和
HibernateTemplate
一樣。果然一樣。
注意到:
Connection con = DataSourceUtils.getConnection(getDataSource());
Connection
是從
DataSourceUtils.getConnection()
來的,繼續跟蹤
DataSourceUtils.getConnection()
。
找到:
ConnectionHolder conHolder = (ConnectionHolder) TransactionSynchronizationManager.getResource(dataSource);
和
Hibernate
中的一模一樣,因為沒有了
session
的封裝,條理在
jdbc
中更加清晰了。
至此,
spring
的事務控制
已經全部搞定。
2?????????
Spring
事務管理的配置
看了上面同事學習
spring
的筆記后自己也覺得有新的理解,從什么地方說起呢?就從
spring
的事務配置說起吧。那么我們看看
contextConfig.xml
吧。
<bean id="sessionFactory" class="org.springframework.orm.hibernate.LocalSessionFactoryBean">
?????? <property name="dataSource">
?????????? <ref bean="dataSource" />
?????? </property>
?????? <property name="mappingResources">
?????????? <list>
????????????? <value>mf/org/user/User.hbm.xml</value>
?????????? </list>
?????? </property>
</bean>
<bean id="transactionManager" class="org.springframework.orm.hibernate.HibernateTransactionManager">
?????? <property name="sessionFactory">
?????????? <ref local="sessionFactory" />
?????? </property>
??? </bean>
<bean id="txProxyTemplate" abstract="true" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
?????? <property name="transactionManager">
?????????? <ref bean="transactionManager" />
?????? </property>
?????? <property name="transactionAttributes">
?????????? <props>
<prop key="save*">PROPAGATION_REQUIRED,-Exception</prop>
<prop key="remove*">PROPAGATION_REQUIRED,-Exception </prop>
<prop key="update*">PROPAGATION_REQUIRED,-Exception </prop>
<prop key="incress*">PROPAGATION_REQUIRED,-Exception </prop>
<prop key="*">PROPAGATION_REQUIRED,readOnly</prop>
?????????? </props>
?????? </property>
??? </bean>
<bean id="userManager" parent="txProxyTemplate">
?????? <property name="target" ref="userManagerTarget" />
</bean>
<bean id="userManagerTarget"
class=" mf.org.hb.user.service.impl.UserManagerImpl">
?????? <property name="userDAO" ref="userDAO" />
</bean>
<bean id="userDAO" class="mf.org.hb.user.dao.hibernate.UserDAOHibernate">
?????? <property name="sessionFactory" ref="sessionFactory" />
</bean>
???
以上就是一個完整的
spring
配置,是不是很熟悉呢,這里是用的
Appfuse
的框架,呵呵。有那么點味道吧。
???
首先我們看看
<bean id="transactionManager" class="org.springframework.orm.hibernate.HibernateTransactionManager">
?????? <property name="sessionFactory">
?????????? <ref local="sessionFactory" />
?????? </property>
</bean>
???
這一個
bean
讓
spring
為我們注入了什么呢?事務,對!我們把
hibernate
的事務注入到了
spring
的
IOC
容器之中了。然后我們再看看:
???
<bean id="txProxyTemplate" abstract="true" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
?????? <property name="transactionManager">
?????????? <ref bean="transactionManager" />
?????? </property>
?????? <property name="transactionAttributes">
?????????? <props>
<prop key="save*">PROPAGATION_REQUIRED,-Exception</prop>
<prop key="remove*">PROPAGATION_REQUIRED,-Exception </prop>
<prop key="update*">PROPAGATION_REQUIRED,-Exception </prop>
<prop key="incress*">PROPAGATION_REQUIRED,-Exception </prop>
<prop key="*">PROPAGATION_REQUIRED,readOnly</prop>
?????????? </props>
?????? </property>
</bean>
???
這個
bean
又是讓
spring
為我們注入了了什么呢?事務代理,對了!我們把事務的代理交給一個
txProxyTemplate
的去做了,這樣的好處我待會再說,現在我們看看下面的一些配置信息。
<prop key="save*">PROPAGATION_REQUIRED,-Exception</prop>
<prop key="remove*">PROPAGATION_REQUIRED,-Exception </prop>
<prop key="update*">PROPAGATION_REQUIRED,-Exception </prop>
<prop key="incress*">PROPAGATION_REQUIRED,-Exception </prop>
<prop key="*">PROPAGATION_REQUIRED,readOnly</prop>
這里就是事務處理時如果遇到異常信息,或者其他的原因時我們要求
spring
把當前的事務回滾了,這樣才能不至于在數據庫中產生垃圾啊。我們規定所有的
save,remove,update,incress
這樣的方法開頭的在出現一些問題后把事務給回滾了,看看我們寫的:
PROPAGATION_REQUIRED,-Exception
。
有人就會說
PROPAGATION_REQUIRED
就可以回滾事務啊,為什么加上
,-Exception
呢?其實我以前也時這樣想的,但這是不完全正確的,當然我們在處理一個事務時只要有一個
PROPAGATION_REQUIRED
就可以了,但是當我們的業務邏輯中要求我們在一個事務代理中開啟兩個事務,這兩個事務表面上沒有聯系,但是實際中又有很多聯系的,比如我們上傳附件和提交文檔,這樣兩個操作我們可以分開,因為他們不是往一個表里插入數據,我們又不希望這兩個操作寫在一個
service
里,這樣我們要是有一個業務只要上傳附件呢?那樣我們是不是又要再寫一個方法啊!所以在開啟兩個事務時如果有一個拋出異常了,我們就要把上一個提交的事務回滾了,這樣做我們就要用的
-Exception
了,這樣就完全滿足我們的要求了,我也試過如果我寫的是
PROPAGATION_REQUIRED,-SQLException
時,這樣我們只會在出現
SQLException
時事務回顧,出現其他的異常事務就不回滾了,好在
spring
可以讓我們寫如異常的基類就可以做到捕獲任何異常,這樣我們就寫
-Exception
好了。特殊情況在特殊處理吧。通用情況下我們還是這樣的。
我們再看看:
<bean id="userManager" parent="txProxyTemplate">
?????? <property name="target" ref="userManagerTarget" />
</bean>
<bean id="userManagerTarget"
class="mf.org.hb.user.service.impl.UserManagerImpl">
?????? <property name="userDAO" ref="userDAO" />
</bean>
<bean id="userDAO" class="mf.org.hb.user.dao.hibernate.UserDAOHibernate">
?????? <property name="sessionFactory" ref="sessionFactory" />
</bean>
???
當然我們也可以寫成:
<bean id="userManager" parent="txProxyTemplate">
?????? <property name="target">
?????????? <bean class="mf.org.hb.user.service.impl.UserManagerImpl">
????????????? <property name="userDAO">
????????????????? <ref bean="userDao"/>
????????????? </property>
?????????? </bean>
?????? </property>
</bean>
<bean id="userDAO" class="mf.org.hb.user.dao.hibernate.UserDAOHibernate">
?????? <property name="sessionFactory" ref="sessionFactory" />
</bean>
這下我們解除以前的疑惑,
parent="txProxyTemplate"
知道我們為什么在上面先寫了
txProxyTemplate
的
bean
了吧,這樣我們就沒有必要再寫一編了。是不是很方便?
spring
的這些技巧還不只這些呢。這樣我們就可以輕松利用以上這三個注入的類去做我們的邏輯了。
Spring
就是要我們注入實現類,然后使用接口操作,這樣耦合性就不是那么強了,這也體現了
Spring
的工廠模式。而
AOP
的
manager
又象我們熟知的代理模式吧
!
3?????????
注意要點
在寫配置的時候注意各個
Manager
和
DAO
之間的關系,以及
<ref=
””
>
之間的關系,清晰里面的關系才能更好的配置。