事务
两种配置事务的方式
1.非注解式事务配置
2. 注解式事务配置
非注解式事务配置
<!-- 基本数据源配置 -->
<bean id="parentDataSource" class="org.apache.commons.dbcp.BasicDataSource">
<property name="maxWait" value="3000"/>
<property name="validationQuery" value="SELECT 1"/>
<property name="testOnBorrow" value="false"/>
<property name="testWhileIdle" value="true"/>
<property name="initialSize" value="100"/>
<property name="maxActive" value="200"/>
<property name="minIdle" value="10"/>
<property name="maxIdle" value="50"/>
<property name="removeAbandoned" value="true"/>
<property name="removeAbandonedTimeout" value="30"/>
<property name="timeBetweenEvictionRunsMillis" value="30000"/>
<property name="driverClassName" value="${feima.jdbc.driver}"/>
</bean>
<bean id="feimaDataSource" parent="parentDataSource">
<property name="url" value="${feima.wr.jdbc.url}"/>
<property name="username" value="${feima.wr.jdbc.username}"/>
<property name="password" value="${feima.wr.jdbc.password}"/>
</bean>
<!-- 配置事务管理器 1.使用dataSourceManager 2.指定dataSource -->
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="feimaDataSource"/>
</bean>
<!--xml 配置-->
<!--配置事务传播特性 1.指明使用哪个事务管理器 2.方法通配符配置传播特性-->
<tx:advice id="transactionInterceptor" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="query*" propagation="SUPPORTS"/>
<tx:method name="get*" propagation="SUPPORTS"/>
<tx:method name="update*" propagation="REQUIRED"/>
<tx:method name="save*" propagation="REQUIRED"/>
<tx:method name="deal*" propagation="REQUIRED"/>
<tx:method name="*" propagation="SUPPORTS"/>
</tx:attributes>
</tx:advice>
<!--配置事务拦截器 1.通配符配置拦截哪些类、哪些方法、哪些返回值、哪些参数 2.指定切入点及通知 -->
<aop:config>
<aop:pointcut id="transactionPointCut" expression="execution(* com.jd.feima.crm.client.impl.remote.*.*(..))"></aop:pointcut>
<aop:advisor advice-ref="transactionInterceptor" pointcut-ref="transactionPointCut"/>
</aop:config>
注解式事务配置
<!-- 注解式事务 启用注解事务 -->
<tx:annotation-driven transaction-manager="transactionManager"/>
事务详解
事务传播特性:
PROPAGATION_REQUIRED--支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择。
PROPAGATION_SUPPORTS--支持当前事务,如果当前没有事务,就以非事务方式执行。
PROPAGATION_MANDATORY--支持当前事务,如果当前没有事务,就抛出异常。
PROPAGATION_REQUIRES_NEW--新建事务,如果当前存在事务,把当前事务挂起。
PROPAGATION_NOT_SUPPORTED--以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
PROPAGATION_NEVER--以非事务方式执行,如果当前存在事务,则抛出异常。
PROPAGATION_NESTED--如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则进行与PROPAGATION_REQUIRED类似的操作。
备注:
可能大家还是对其中几个有点混淆,还是补充说明一下:PROPAGATION_NESTED: 嵌套事务类型,是相对上面提到的六种情况(上面的六种应该称为平面事务类型),打个比方我现在有一个事务主要有一下几部分:
1,从A用户帐户里面减去100元钱
2,往B用户帐户里面添加100元钱
这样看和以前不同的事务可能没有什么区别,那我现在有点特殊的要求就是,A用户有3个帐户,B用户有2个帐户,现在我的要求就是只要再A用户的3个帐户里面任意一个减去100元,往B用户的两个帐户中任意一个里面增加100元就可以了!
一旦你有这样的要求那嵌套事务类型就非常适合你!我们可以这样理解,
一:将“从A用户帐户里面减去100元钱” 和 “往B用户帐户里面增加100元钱”我们暂时认为是一级事务操作
二:将从A用户的3个帐户的任意一个帐户里面减钱看做是“从A用户帐户里面减去100元钱”这个一级事务的子事务(二级事务),同样把后面存钱的看成是另一个的二级事务。
问题一:当二级事务失败被rollback一级事务会不会被rollback?
答案:如果二级事务被trycatch , 一级事务是不会被rollback的
问题二:什么时候这个一级事务会commit,什么时候会被rollback呢?
我们主要看二级里面出现的情况,当所有的二级事务被commit了并且一级事务没有失败的操作,那整个事务就算是一个成功的事务,这种情况整个事务会被commit。
当任意一个二级事务没有被commit, 且在一级事务里没有被try catch,那整个事务就是失败的,整个事务会被roolback。
还是拿上面的例子来说明吧!如果我在a的三个帐户里面减钱的操作都被二级事务给rollback了,也就是3个帐户里面都没有减钱成功,整个事务就失败了就会被rollback。如果A用户帐户三个帐户里面有一个可以扣钱而且B用户的两个帐户里面也有一个帐户可以增加钱,那整个事务就算成功的,会被 commit。
比较
PROPAGATION_REQUIRED--支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择。
PROPAGATION_REQUIRES_NEW--新建事务,如果当前存在事务,把当前事务挂起。
PROPAGATION_NESTED--如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则进行与PROPAGATION_REQUIRED类似的操作。
-
ServiceA {
-
/**
-
* 事务属性配置为 PROPAGATION_REQUIRED
-
*/
-
voidmethodA(){insertA(a) ;
-
try {
-
ServiceB.methodB();
-
} catch (SomeException) {
-
// 执行其他业务, 如 ServiceC.methodC();
-
}
-
}
-
}B异常了
a插入成功
b中异常之前的数据未回滚
c执行
//就是说B支持A的事务, B异常了,但A中trycatch了, 相当于事务commit
}else if(A== required && B=requiredNew) {
a插入成功
B回滚
c执行
}else if(A== required && B=nested) {
a成功
b回滚
c.执行
}
-
A异常了
全部回滚
}else if(A== required && B=requiredNew) {
a回滚
B成功
c回滚
}else if(A== required && B=nested) {
全部回滚
}