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=
””
>
之間的關系,清晰里面的關系才能更好的配置。