2020-08-12 spring事务

2 篇文章 0 订阅
http://blog.csdn.net/yuanlaishini2010/article/details/45792069

事务的嵌套概念
所谓事务的嵌套就是两个事务方法之间相互调用。
spring事务开启 ,或者是基于接口的或者是基于类的代理被创建(注意一定要是代理,不能手动new 一个对象,
并且此类(有无接口都行)一定要被代理——spring中的bean只要纳入了IOC管理都是被代理的)。
所以在同一个类中一个方法调用另一个方法有事务的方法,事务是不会起作用的。

###
Spring默认情况下会对运行期例外(RunTimeException),即uncheck异常,进行事务回滚。
如果遇到checked异常就不回滚。
如何改变默认规则:

1 让checked例外也回滚:在整个方法前加上 @Transactional(rollbackFor=Exception.class)
2 让unchecked例外不回滚: @Transactional(notRollbackFor=RunTimeException.class)
3 不需要事务管理的(只查询的)方法:@Transactional(propagation=Propagation.NOT_SUPPORTED)

上面三种方式也可在xml配置

spring事务传播属性 在 spring的 TransactionDefinition接口中一共定义了六种事务传播属性:

propagation_required      -- 支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择。 
propagation_supports      -- 支持当前事务,如果当前没有事务,就以非事务方式执行。 
propagation_mandatory     -- 支持当前事务,如果当前没有事务,就抛出异常。 
propagation_requires_new  -- 新建事务,如果当前存在事务,把当前事务挂起。 
propagation_not_supported -- 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。 
propagation_never         -- 以非事务方式执行,如果当前存在事务,则抛出异常。 
propagation_nested        -- 如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,
则进行与propagation_required类似的操作。
 
前六个策略类似于ejb cmt,第七个(propagation_nested)是spring所提供的一个特殊变量。 
它要求事务管理器或者使用jdbc 3.0 savepoint api提供嵌套事务行为(如spring的datasourcetransactionmanager) 


举例浅析Spring嵌套事务
ServiceA#methodA(我们称之为外部事务),ServiceB#methodB(我们称之为外部事务)
 
ServiceA {   
     void methodA() {
         ServiceB.methodB();
     }  
}
  
ServiceB {       
     void methodB() {
     }       
}

propagation_required
假如当前正要执行的事务不在另外一个事务里,那么就起一个新的事务 
比如说,serviceb.methodb的事务级别定义为propagation_required, 那么由于执行servicea.methoda的时候
  1、如果servicea.methoda已经起了事务,这时调用serviceb.methodb,serviceb.methodb看到自己已经运行在servicea.methoda的事务内部,
就不再起新的事务。这时只有外部事务并且他们是共用的,
所以这时servicea.methoda或者serviceb.methodb无论哪个发生异常methoda和methodb作为一个整体都将一起回滚。
  2、如果servicea.methoda没有事务,serviceb.methodb就会为自己分配一个事务。
这样,在servicea.methoda中是没有事务控制的。只是在serviceb.methodb内的任何地方出现异常,serviceb.methodb将会被回滚,
不会引起servicea.methoda的回滚

propagation_supports
如果当前在事务中,即以事务的形式运行,如果当前不再一个事务中,那么就以非事务的形式运行 
propagation_mandatory
必须在一个事务中运行。也就是说,他只能被一个父事务调用。否则,他就要抛出异常
propagation_requires_new
启动一个新的, 不依赖于环境的 "内部" 事务. 这个事务将被完全 commited 或 rolled back 而不依赖于外部事务, 
它拥有自己的隔离范围, 自己的锁, 等等. 当内部事务开始执行时, 外部事务将被挂起, 内务事务结束时, 外部事务将继续执行. 
 比如我们设计servicea.methoda的事务级别为propagation_required,serviceb.methodb的事务级别为propagation_requires_new,那么当执行到serviceb.methodb的时候,servicea.methoda所在的事务就会挂起,serviceb.methodb会起一个新的事务,等待serviceb.methodb的事务完成以后,他才继续执行。他与propagation_required 的事务区别在于事务的回滚程度了。因为serviceb.methodb是新起一个事务,那么就是存在两个不同的事务。
1、如果serviceb.methodb已经提交,那么servicea.methoda失败回滚,serviceb.methodb是不会回滚的。
2、如果serviceb.methodb失败回滚,如果他抛出的异常被servicea.methoda的try..catch捕获并处理,servicea.methoda事务仍然可能提交;如果他抛出的异常未被servicea.methoda捕获处理,servicea.methoda事务将回滚。

使用场景:
不管业务逻辑的service是否有异常,log service都应该能够记录成功,所以log service的传播属性可以配为此属性。最下面将会贴出配置代码。

propagation_not_supported
当前不支持事务。比如servicea.methoda的事务级别是propagation_required ,而serviceb.methodb的事务级别是propagation_not_supported ,那么当执行到serviceb.methodb时,servicea.methoda的事务挂起,而他以非事务的状态运行完,再继续servicea.methoda的事务。
propagation_never
不能在事务中运行。假设servicea.methoda的事务级别是propagation_required, 而serviceb.methodb的事务级别是propagation_never ,那么serviceb.methodb就要抛出异常了。 
propagation_nested
开始一个 "嵌套的" 事务,  它是已经存在事务的一个真正的子事务. 潜套事务开始执行时,  它将取得一个 savepoint. 如果这个嵌套事务失败, 我们将回滚到此 savepoint. 潜套事务是外部事务的一部分, 只有外部事务结束后它才会被提交. 

比如我们设计servicea.methoda的事务级别为propagation_required,serviceb.methodb的事务级别为propagation_nested,那么当执行到serviceb.methodb的时候,servicea.methoda所在的事务就会挂起,serviceb.methodb会起一个新的子事务并设置savepoint,等待serviceb.methodb的事务完成以后,他才继续执行。。因为serviceb.methodb是外部事务的子事务,那么
1、如果serviceb.methodb已经提交,那么servicea.methoda失败回滚,serviceb.methodb也将回滚。
2、如果serviceb.methodb失败回滚,如果他抛出的异常被servicea.methoda的try..catch捕获并处理,servicea.methoda事务仍然可能提交;如果他抛出的异常未被servicea.methoda捕获处理,servicea.methoda事务将回滚。

理解nested的关键是savepoint。他与propagation_requires_new的区别是:
propagation_requires_new 完全是一个新的事务,它与外部事务相互独立; 而 propagation_nested 则是外部事务的子事务, 如果外部事务 commit, 嵌套事务也会被 commit, 这个规则同样适用于 roll back. 

在 spring 中使用 propagation_nested的前提:
1. 我们要设置 transactionmanager 的 nestedtransactionallowed 属性为 true, 注意, 此属性默认为 false!!! 
2. java.sql.savepoint 必须存在, 即 jdk 版本要 1.4+ 
3. connection.getmetadata().supportssavepoints() 必须为 true, 即 jdbc drive 必须支持 jdbc 3.0 

确保以上条件都满足后, 你就可以尝试使用 propagation_nested 了. 


##############################################################################
Log Service配置事务传播
不管业务逻辑的service是否有异常,Log Service都应该能够记录成功,通常有异常的调用更是用户关心的。
Log Service如果沿用业务逻辑Service的事务的话在抛出异常时将没有办法记录日志(事实上是回滚了)。
所以希望Log Service能够有独立的事务。日志和普通的服务应该具有不同的策略。Spring 配置文件transaction.xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="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.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd">
    <!-- configure transaction -->
 
    <tx:advice id="defaultTxAdvice" transaction-manager="transactionManager">
        <tx:attributes>
            <tx:method name="get*" read-only="true" />
            <tx:method name="query*" read-only="true" />
            <tx:method name="find*" read-only="true" />
            <tx:method name="*" propagation="REQUIRED" rollback-for="java.lang.Exception" />
        </tx:attributes>
    </tx:advice>
 
    <tx:advice id="logTxAdvice" transaction-manager="transactionManager">
        <tx:attributes>
            <tx:method name="get*" read-only="true" />
            <tx:method name="query*" read-only="true" />
            <tx:method name="find*" read-only="true" />
            <tx:method name="*" propagation="REQUIRES_NEW"
                rollback-for="java.lang.Exception" />
        </tx:attributes>
    </tx:advice>
 
    <aop:config>
        <aop:pointcut id="defaultOperation"
            expression="@within(com.homent.util.DefaultTransaction)" />
        <aop:pointcut id="logServiceOperation"
            expression="execution(* com.homent.service.LogService.*(..))" />
            
        <aop:advisor advice-ref="defaultTxAdvice" pointcut-ref="defaultOperation" />
        <aop:advisor advice-ref="logTxAdvice" pointcut-ref="logServiceOperation" />
    </aop:config>
</beans>

 如上面的Spring配置文件所示,日志服务的事务策略配置为propagation="REQUIRES_NEW",
告诉Spring不管上下文是否有事务,Log Service被调用时都要求一个完全新的只属于Log Service自己的事务。
通过该事务策略,Log Service可以独立的记录日志信息,不再受到业务逻辑事务的干扰。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值