javascript
Spring学习8-Spring事务管理(AOP/声明式式事务管理)
聲明式事務的事務屬性:
一:傳播行為
二:隔離級別
三:只讀提示
四:事務超時間隔
五:異常:指定除去RuntimeException其他回滾異常。
?傳播行為:
所謂事務的傳播行為是指,如果在開始當前事務之前,一個事務上下文已經存在,此時有若干選項可以指定一個事務性方法的執行行為。
spring的事務傳播規則:
| 傳播行為 | 意義 |
| PROPAGATION_REQUIRED | 如果當前存在事務,則加入該事務;如果當前沒有事務,則創建一個新的事務。 |
| PROPAGATION_REQUIRES_NEW | 創建一個新的事務,如果當前存在事務,則把當前事務掛起。 |
| PROPAGATION_SUPPORTS | 如果當前存在事務,則加入該事務;如果當前沒有事務,則以非事務的方式繼續運行。 |
| PROPAGATION_NOT_SUPPORTED | 以非事務方式運行,如果當前存在事務,則把當前事務掛起。 |
| PROPAGATION_NEVER | 以非事務方式運行,如果當前存在事務,則拋出異常 |
| PROPAGATION_MANDATORY | 如果當前存在事務,則加入該事務;如果當前沒有事務,則拋出異常。 |
| PROPAGATION_NESTED | 如果當前存在事務,則創建一個事務作為當前事務的嵌套事務來運行;如果當前沒有事務,則該取值等價于TransactionDefinition.PROPAGATION_REQUIRED。 |
隔離級別:
隔離級別是指若干個并發的事務之間的隔離程度:
spring的事務隔離級別:?
| 隔離級別 | 含義 |
| ISOLATION_DEFAULT | 這是默認值,表示使用底層數據庫的默認隔離級別。對大部分數據庫而言,通常這值就是ISOLATION_READ_COMMITTED。 |
| ISOLATION_READ_UNCOMMITTED | 該隔離級別表示一個事務可以讀取另一個事務修改但還沒有提交的數據。該級別不能防止臟讀和不可重復讀,因此很少使用該隔離級別。 |
| ISOLATION_READ_COMMITTED | 該隔離級別表示一個事務只能讀取另一個事務已經提交的數據。該級別可以防止臟讀,這也是大多數情況下的推薦值。 |
| ISOLATION_REPEATABLE_READ | 該隔離級別表示一個事務在整個過程中可以多次重復執行某個查詢,并且每次返回的記錄都相同。即使在多次查詢之間有新增的數據滿足該查詢,這些新增的記錄也會被忽略。該級別可以防止臟讀和不可重復讀。 |
| ISOLATION_SERIALIZABLE | 所有的事務依次逐個執行,這樣事務之間就完全不可能產生干擾,也就是說,該級別可以防止臟讀、不可重復讀以及幻讀。但是這將嚴重影響程序的性能。通常情況下也不會用到該級別。 |
SessionFlush設置為none(readOnly);
事務的只讀屬性是指,對事務性資源進行只讀操作或者是讀寫操作。所謂事務性資源就是指那些被事務管理的資源,比如數據源、 JMS資源,以及自定義的事務性資源等等。如果確定只對事務性資源進行只讀操作,那么我們可以將事務標志為只讀的,以提高事務處理的性能。在TransactionDefinition 中以 boolean 類型來表示該事務是否只讀。
事務超時間隔:
所謂事務超時,就是指一個事務所允許執行的最長時間,如果超過該時間限制但事務還沒有完成,則自動回滾事務。在TransactionDefinition 中以 int 的值來表示超時時間,其單位是秒。
聲明式事務使用范例:
??二、聲明式事務配置的5中方式(方式四最常用)
?? 方式一、基于Spring1.x,使用TransactionProxyFactoryBean為每個Bean生成一個代理。
????<?xml version="1.0"encoding="UTF-8"?>
<beansxmlns="http://www.springframework.org/schema/beans"
???xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
???xmlns:aop="http://www.springframework.org/schema/aop"
???xsi:schemaLocation="http://www.springframework.org/schema/beans
??????????http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
??????????http://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop-2.5.xsd">
???<bean id="sessionFactory"
???????????class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
???????<property name="configLocation"value="classpath:hibernate.cfg.xml" />
???????<property name="configurationClass"value="org.hibernate.cfg.AnnotationConfiguration"/>
???</bean>
???<!-- 定義事務管理器(聲明式的事務) -->
???<beanid="transactionManager"??????class="org.springframework.orm.hibernate3.HibernateTransactionManager">
???????<property name="sessionFactory" ref="sessionFactory"/>
???</bean>
??
???<!-- 配置DAO省略 -->
??? <!--*******業務邏輯層(是對各個DAO層的正面封裝)主要用到<<門面模式>>******-->
????<bean id="fundService"
?????class="com.jack.fund.service.serviceimpl.FundService">
?????<property name="producedao">
??????<ref bean="fundProduceDAO" />
?????</property>
????</bean>
??
????<beanid="fundServiceProxy"
?????class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
?????<!-- 配置事務管理器 -->
?????<propertyname="transactionManager">
??????<ref bean="transactionManager"/>
?????</property>
?????<!-- 此屬性指定目標類本本身是否是代理的對象,如果目標類沒有實現任何類,就設為true代表自己 可省略不寫-->
?????<propertyname="proxyTargetClass">
??????<value>false</value>
?????</property>
?????<!-- 代理接口? 可省略不寫-->
?????<propertyname="proxyInterfaces">
??????<value>com.jack.fund.service.IFundService</value>
?????</property>
?????<!-- 目標bean -->
?????<property name="target">
??????<ref bean="fundService" />
?????</property>
?????<!-- 配置事務屬性 -->
?????<propertyname="transactionAttributes">
??????<props>
???????<propkey="delete*">PROPAGATION_REQUIRED</prop>
???????<propkey="add*">PROPAGATION_REQUIRED</prop>
???????<propkey="update*">PROPAGATION_REQUIRED</prop>
???????<propkey="save*">PROPAGATION_REQUIRED</prop>
???????<prop??key="find*">PROPAGATION_REQUIRED,readOnly</prop>
??????</props>
?????</property>
????</bean>
</beans>
????可以看出 它適用于你的庫表比較少的情況下,針對每一個功能模塊配置一個業務代理服務。如果模塊多大話,就顯得代碼有點多了,發現他們只是稍微一點不一樣。這時我們就應該想到繼承的思想。用第二種方法。
?
方式二、基于Spring1.x,使用TransactionProxyFactoryBean為所有Bean共享一個代理基類。
???<?xml version="1.0"encoding="UTF-8"?>
<beansxmlns="http://www.springframework.org/schema/beans"
???xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
???xmlns:aop="http://www.springframework.org/schema/aop"
???xsi:schemaLocation="http://www.springframework.org/schema/beans
??????????http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
??????????http://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop-2.5.xsd">
???<bean id="sessionFactory"
???????????class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
???????<property name="configLocation"value="classpath:hibernate.cfg.xml" />
???????<property name="configurationClass"value="org.hibernate.cfg.AnnotationConfiguration"/>
???</bean>
???<!-- 定義事務管理器(聲明式的事務) -->
???<bean id="transactionManager"
???????class="org.springframework.orm.hibernate3.HibernateTransactionManager">
???????<property name="sessionFactory" ref="sessionFactory"/>
???</bean>
??
????<!--利用繼承的思想簡化配置,要把abstract="true"-->
????<beanid="transactionBase"???class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"
?????lazy-init="true" abstract="true">
?????<!-- 配置事務管理器 -->
?????<propertyname="transactionManager">
??????<ref bean="transactionManager"/>
?????</property>
?????<!-- 配置事務屬性 -->
?????<propertyname="transactionAttributes">
??????<props>
???????<propkey="delete*">PROPAGATION_REQUIRED</prop>
???????<propkey="add*">PROPAGATION_REQUIRED</prop>
???????<propkey="update*">PROPAGATION_REQUIRED</prop>
???????<propkey="save*">PROPAGATION_REQUIRED</prop>
???????<propkey="find*">PROPAGATION_REQUIRED,readOnly</prop>
??????</props>
?????</property>
????</bean>
??? <!--而具體的模塊可以簡單的這樣配置。只要指明它的parent(父類)就可以了。父類一般把abstract="true",因為在容器加載的時候不需要初始化,等到用的時候再有它的子類調用的時候,再去初始化。? -->
? <!-- 配置DAO省略-->
???<!--*******業務邏輯層(是對各個DAO層的正面封裝)主要用到<<門面模式>>******-->
????<bean id="fundService"
?????class="com.jack.fund.service.serviceimpl.FundService">
?????<property name="producedao">
??????<ref bean="fundProduceDAO" />
?????</property>
????</bean>
??? <bean id="fundServiceProxy"parent="transactionBase" >
?????<property name="target">
?????<ref bean="fundService" />
?????</property>
????</bean>
</beans>
這樣配置的話,如果有多個像fundService這樣模塊時,可以少些很多重復的代碼。
方式三、基于SpringAOP的攔截器(主要利用BeanNameAutoProxyCreator自動創建事務代理)
<?xml version="1.0"encoding="UTF-8"?>
<beansxmlns="http://www.springframework.org/schema/beans"
???xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
???xmlns:aop="http://www.springframework.org/schema/aop"
???xsi:schemaLocation="http://www.springframework.org/schema/beans
??????????http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
??????????http://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop-2.5.xsd">
???<bean id="sessionFactory"
???????????class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
???????<property name="configLocation"value="classpath:hibernate.cfg.xml" />
???????<property name="configurationClass"value="org.hibernate.cfg.AnnotationConfiguration"/>
???</bean>
???<!-- 定義事務管理器(聲明式的事務) -->
???<bean id="transactionManager"
???????class="org.springframework.orm.hibernate3.HibernateTransactionManager">
???????<property name="sessionFactory" ref="sessionFactory"/>
???</bean>
??? <bean id="transactionInterceptor"????class="org.springframework.transaction.interceptor.TransactionInterceptor">
???????<property name="transactionManager"ref="transactionManager" />
???????<!-- 配置事務屬性 -->
?????<propertyname="transactionAttributes">
??????<props>
???????<propkey="delete*">PROPAGATION_REQUIRED</prop>
???????<propkey="add*">PROPAGATION_REQUIRED</prop>
???????<propkey="update*">PROPAGATION_REQUIRED</prop>
???????<propkey="save*">PROPAGATION_REQUIRED</prop>
???????<propkey="find*">PROPAGATION_REQUIRED,readOnly</prop>
??????</props>
?????</property>
???</bean>???
??? <beanclass="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
???????<property name="beanNames">
???????????<list>
???????????<!-- 匹配所有的Service或者某個具體Service-->
???????????????<value>*Service</value>
???????????</list>
???????</property>
???????<propertyname="interceptorNames">
???????????<list>
???????????????<value>transactionInterceptor</value>
???????????</list>
???????</property>
???</bean>
????<!-- 配置DAO省略 -->
???<!--*******業務邏輯層(是對各個DAO層的正面封裝)主要用到<<門面模式>>******-->
????<bean id="fundService"
?????class="com.jack.fund.service.serviceimpl.FundService">
?????<property name="producedao">
??????<ref bean="fundProduceDAO" />
?????</property>
????</bean>
</beans>
方式四、基于Spring2.x,使用tx標簽配置的攔截器
?<?xml version="1.0"encoding="UTF-8"?>
<beansxmlns="http://www.springframework.org/schema/beans"
???xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
???xmlns:aop="http://www.springframework.org/schema/aop"
???xmlns:tx="http://www.springframework.org/schema/tx"
???xsi:schemaLocation="http://www.springframework.org/schema/beans
??????????http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
??????????http://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop-2.5.xsd
??????????http://www.springframework.org/schema/txhttp://www.springframework.org/schema/tx/spring-tx-2.5.xsd">
? ? <beanid="sessionFactory"?????class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
???????<property name="configLocation"value="classpath:hibernate.cfg.xml" />
???????<property name="configurationClass"value="org.hibernate.cfg.AnnotationConfiguration"/>
???</bean>
???<!-- 定義事務管理器(聲明式的事務) -->
???<bean id="transactionManager"?class="org.springframework.orm.hibernate3.HibernateTransactionManager">
???????<property name="sessionFactory" ref="sessionFactory"/>
???</bean>
?<!--??定義JDBC的事務管理器
? ? ?<bean id="transactionManager"class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
???????<property name="dataSource" ref="dataSource"/>
???</bean>
-->
<!-- 定義切面-->
???<tx:advice id="txAdvice"transaction-manager="transactionManager">
???????<tx:attributes>
???????????<tx:method name="get*" read-only="true"rollback-for="java.lang.Exception"/>
????????????????<!--其他方法設為如下傳播屬性-->
???????????<tx:method name="*" propagation="REQUIRED"rollback-for="java.lang.Exception"/>?
??????????</tx:attributes>
???</tx:advice>
<!--定義切點和織入-->??
??? <aop:config>
???????<aop:pointcut id="interceptorPointCuts"
???????????expression="execution(* com.spring.service.*.*(..))" />
???????<!-- mgrService和 empService省略?-->
????????<aop:pointcut id="leePointcut"
? ? ?? ? ?expression="bean(mgrService) || bean(empService)" />
???????<aop:advisor advice-ref="txAdvice"
???????????pointcut-ref="interceptorPointCuts"/>
???????<aop:advisor advice-ref="txAdvice"
???????????pointcut-ref=" leePointcut"/>
? ? ?? <!-- 將authority轉換成切面Bean
?????????切面Bean的新名稱為:authorityAspect-->
????? ?<aop:aspectid="authorityAspect" ref="authority">
?????????<!--定義一個Around增強處理,直接指定切入點表達式
????????????以切面Bean中的authority()方法作為增強處理方法-->
?????????<aop:around pointcut="execution(*org.crazyjava.message.service.impl.*Impl.*Message*(..))"
????????????method="authority"/>
??????</aop:aspect>???
???</aop:config>
<!-- 定義一個普通Bean實例,該Bean實例將進行權限控制-->
???<bean id="authority"
???class="org.crazyjava.message.authority.AuthorityInterceptor"/>? ?
</beans>
?關于權限攔截的類AuthorityInterceptor定義如下:
?public class AuthorityInterceptor
{
???//進行權限檢查的方法
??? publicObject authority(ProceedingJoinPoint jp)
?????? throwsjava.lang.Throwable
??? {
?????? HttpSessionsess = null;
??????//獲取被攔截方法的全部參數
?????? Object[]args = jp.getArgs();
??????//遍歷被攔截方法的全部參數
?????? for (int i =0 ; i < args.length ; i++ )
?????? {
?????????//找到HttpSession類型的參數
????????? if (args[i]instanceof HttpSession) sess =
????????????(HttpSession)args[i];
?????? }
??????//取出HttpSession里的user屬性
?????? IntegeruserId = (Integer)sess.getAttribute("user");
??????//如果HttpSession里的user屬性不為null,且大于0
?????? if ( userId!= null && userId >0)
?????? {
????????? //繼續處理
????????? returnjp.proceed(args);
?????? }
?????? else
?????? {
?????????//如果還未登錄,拋出異常
????????? throw newMessageException("您還沒有登陸,請先登陸系統再執行該操作");
?????? }
??? }
}
方式五、注解式聲明事務(詳見下篇博文)
其實他們還可以混合使用,方式三和方式四的混合使用:
<?xml version="1.0"encoding="UTF-8"?>
<beansxmlns="http://www.springframework.org/schema/beans"
???xmlns:aop="http://www.springframework.org/schema/aop"
???xmlns:tx="http://www.springframework.org/schema/tx"
???xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
???xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans-2.5.xsd
???http://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop-2.5.xsd
???http://www.springframework.org/schema/txhttp://www.springframework.org/schema/tx/spring-tx-2.5.xsd">
<!--?? 其他省略
??????********************
??????????? AOP通知
??????********************
????-->
??? <bean id="exception"class="com.etc.advisor.ExceptionAdvisor"></bean>
???<bean id="tx"class="org.springframework.transaction.interceptor.TransactionInterceptor">
??????<constructor-arg index="0">
?????????<ref bean="tm"/>
??????</constructor-arg>
??????<constructor-arg index="1">
?????????<props>
????????????<propkey="*">PROPAGATION_REQUIRED</prop>
?????????</props>
??????</constructor-arg>
???</bean>
???
???<aop:configproxy-target-class="true">
??????<aop:pointcut id="p1" expression="(execution(*com.etc.action.*.execute()))" />
??????<aop:pointcut id="p2" expression="(execution(*com.etc.*.*.*(..)))" />
??????<aop:pointcut id="p3" expression="(execution(*com.etc.service.*.*(..)))" /><!--service的所有方法 -->
??????<aop:advisor advice-ref="tx"pointcut-ref="p1"/>
??????<aop:advisor advice-ref="exception"pointcut-ref="p2"/>
???</aop:config>
</beans>
??三、Spring事務配置示例
步驟一、配置文件(applicationContext.xml):
???<?xml version="1.0"encoding="UTF-8"?>
?<beansxmlns="http://www.springframework.org/schema/beans"
????xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:p="http://www.springframework.org/schema/p"
????xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
?
????<bean id="userservice"class="com.sunflower.yuan.serviceimp.UserServiceImp">
????????<property name="jdbcTemplate">
????????????<ref bean="jdbcTemplate"/>
????????</property>
????</bean>
????
????<bean id="datasource"class="org.springframework.jdbc.datasource.DriverManagerDataSource">
????????<property name="driverClassName"value="com.mysql.jdbc.Driver"></property>
????????<property name="url"value="jdbc:mysql://localhost:3306/mytest"></property>
????????<property name="username"value="root"></property>
????????<property name="password"value="root"></property>
????</bean>
????
????<bean id="transactionManager"class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
????????<property name="dataSource">
????????????<ref bean="datasource"/>
????????</property>
????</bean>
????
????<bean id="jdbcTemplate"class="org.springframework.jdbc.core.JdbcTemplate">
????????<property name="dataSource">
????????????<ref bean="mydatasource"/>
????????</property>
????</bean>
????
????
????
???? <beanid="transactionAttributeSource"??class="org.springframework.transaction.interceptor.
????????NameMatchTransactionAttributeSource">
????????<property name="properties">
????????????<props>
????????????????<prop key="getMoney">
????????????????????PROPAGATION_REQUIRED,ISOLATION_DEFAULT,-NoMoneyException
????????????????</prop>
????????????</props>
????????</property>
????</bean>
????
????
????<!-- 聲明式事務管理的代理類-->
????<beanid="userTransaction"???????
? class="org.springframework.transaction.interceptor.??TransactionProxyFactoryBean">
????????<propertyname="proxyInterfaces">
????????????<list>
????????????????<value>com.sunflower.yuan.servicedao.UserService</value>
????????????</list>
????????</property>
????????<property name="target">
????????????<ref bean="userservice"/>
????????</property>
????????<propertyname="transactionManager">
????????????<ref bean="transactionManager"/>
????????</property>
????????<propertyname="transactionAttributeSource">
????????????<refbean="transactionAttributeSource"/>
????????</property>
????</bean>
?</beans>
聲明式事務管理也是用動態代理模式來實現的,思想和AOP是一樣的。主要是配置org.springframework.transaction.interceptor.TransactionProxyFactoryBean類。這個配置和AOP的配置非常相似。可以將事務屬性單獨寫在org.springframework.transaction.interceptor.NameMatchTransactionAttributeSource中,然后再用<propertyname="transactionAttributeSource">引入,也可以直接用<propertyname="transactionAttributes">配置事務屬性。
<propkey="getMoney">標簽中的key="getMoney"表示對getMoney方法進行事務管理,也可使用通配符get*,表示對所有get前綴的方法進行事務管理。
事務屬性的配置用","隔開,如下所示:
PROPAGATION, ISOLATION, readonly, -Exception, +Exception
PROPAGATION: 傳播行為
ISOLATION: 隔離級別
readonly:? 是否為只讀事務(可選)
(+/-)Exception:? 回滾規則。如果是"-",遇到指定的Exception,事務回滾,如果是"+",事務不回滾。
UserService.java:
public interface UserService {
????public void getMoney(int money, int id) throwsNoMoneyException;
?}
UserServiceImp.java:
package com.sunflower.yuan.serviceimp;
?
?import java.sql.ResultSet;
?import java.sql.SQLException;
?
?importorg.springframework.jdbc.core.JdbcTemplate;
?importorg.springframework.jdbc.core.RowCallbackHandler;
?
?importcom.sunflower.yuan.Exception.NoMoneyException;
?importcom.sunflower.yuan.servicedao.UserService;
?
?
?public class UserServiceImp implementsUserService {
????private JdbcTemplate jdbcTemplate;
?
????public JdbcTemplate getJdbcTemplate() {
????????return jdbcTemplate;
????}
?
????public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
????????this.jdbcTemplate = jdbcTemplate;
????}
?
????@Override
????public void getMoney(int getMoney, int id) throws NoMoneyException{
?
????????String sql = "select money from user where id=?";
????????Object[] params = new Object[] { new Integer(id) };
?
????????MyRowCallbackHandler rowCallBack = newMyRowCallbackHandler();
????????jdbcTemplate.query(sql, params, rowCallBack);
?
????????if (rowCallBack.getMoney() > getMoney) {
????????????sql = "update user set money=? where id=?";
????????????Object[] params2 = new Object[] {
????????????????????rowCallBack.getMoney() - getMoney, new Integer(id) };
????????????jdbcTemplate.update(sql, params2);
????????????throw new NoMoneyException("余額不足");
????????}
?
????}
?
????class MyRowCallbackHandler implements RowCallbackHandler {
????????private int money = 0;
?
????????public int getMoney() {
????????????return money;
????????}
?
????????public void setMoney(int money) {
????????????this.money = money;
????????}
?
????????@Override
????????public void processRow(ResultSet rs) throws SQLException {
????????????this.money = rs.getInt("money");
????????}
????}
?}
這里要注意一點,要進行事務管理的方法,必須在方法外進行異常的拋出,這樣事務管理器才能接收到,然后進行事務的回滾。如果用try-catch處理異常,將不會進行事務回滾。
總結
以上是生活随笔為你收集整理的Spring学习8-Spring事务管理(AOP/声明式式事务管理)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 面向切面(AOP)之Spring接口方式
- 下一篇: spring4声明式事务--01注解方式