spring事务不回滚
2017-03-02 00:00
127 查看
摘要:spring事务不回滚
spring事务拦截,要注意service里面的方法名称是否在事务配置里面
<tx:adviceid="txAdvice"transaction-manager="transactionManager">
<tx:attributes>
<tx:methodname="delete*"propagation="REQUIRED"read-only="false"
rollback-for="java.lang.Exception"/>
<tx:methodname="insert*"propagation="REQUIRED"read-only="false"
rollback-for="java.lang.Exception"/>
<tx:methodname="update*"propagation="REQUIRED"read-only="false"
rollback-for="java.lang.Exception"/>
<tx:methodname="save*"propagation="REQUIRED"read-only="false"
rollback-for="java.lang.Exception"/>
<tx:methodname="do*"propagation="REQUIRED"read-only="false"
rollback-for="java.lang.Exception"/>
</tx:attributes>
</tx:advice>
service的方法要以deleteinsertupdatesavedo开头事务才会生效
http://www.jianshu.com/p/f5fc14bde8a0
1.Service类内部方法调用
大概就是Service中有一个方法A,会内部调用方法B,方法A没有事务管理,方法B采用了声明式事务,通过在方法上声明Transactional的注解来做事务管理。
总结,在方法A中调用方法B,实际上是通过“this”的引用,也就是直接调用了目标类的方法,而非通过Spring上下文获得的代理类,所以事务是不会开启的。
2.try...catch异常
在一段业务逻辑中对数据库异常进行了处理,使用了try...catch子句捕获异常并throw了一个自定义异常,这种情况导致了事务未回滚,示例代码如下:
BizException的定义如下:
上面代码中的声明式事务在出现异常的时候,事务是不会回滚的。在代码中我虽然捕获了异常,但是同时我也抛出了异常,为什么事务未回滚呢?猜测是异常类型不对,于是开始查询原因,翻看了Spring的官方文档,找到了答案。下面是翻译自Spring官网。
17.5.3声明式事务的回滚
上一节中介绍了如何设置开启Spring事务,一般在你的应用的Service层代码中设置,这一节将介绍在简单流行的声明式事务中如何控制事务回滚。
在SpringFrameWork的事务框架中推荐的事务回滚方法是,在当前执行的事务上下文中抛出一个异常。如果异常未被处理,当抛出异常调用堆栈的时候,SpringFrameWork的事务框架代码将捕获任何未处理的异常,然后并决定是否将此事务标记为回滚。
在默认配置中,SpringFrameWork的事务框架代码只会将出现runtime,unchecked异常的事务标记为回滚;也就是说事务中抛出的异常时RuntimeException或者是其子类,这样事务才会回滚(默认情况下Error也会导致事务回滚)。在默认配置的情况下,所有的checked异常都不会引起事务回滚。
注:UncheckedException包括Error与RuntimeException.RuntimeException的所有子类也都属于此类。另一类就是checkedException。
你可以精确的配置异常类型,指定此异常类事务回滚,包括checked异常。下面的xml代码片段展示了如何配置checked异常引起事务回滚,应用自定义异常类型:
在你遇到异常不想回滚事务的时候,同样的你也可指定不回滚的规则,下面的一个例子告诉你,即使遇到未处理的InstrumentNotFoundException异常时,SpringFrameWork的事务框架同样会提交事务,而不回滚。
还有更灵活的回滚规则配置方法,同时指定什么异常回滚,什么异常不回滚。当SpringFrameWork的事务框架捕获到一个异常的时候,会去匹配配置的回滚规则来决定是否标记回滚事务,使用匹配度最强的规则结果。因此,下面的配置例子表达的意思是,除了异常InstrumentNotFoundException之外的任何异常都会导致事务回滚。
你也可以通过编程式的方式回滚一个事务,尽管方法非常简单,但是也有非常强的代码侵入性,使你的业务代码和SpringFrameWork的事务框架代码紧密的绑定在一起,示例代码如下:
看完官方文档这节内容找到了问题的答案,原来是因为我们自定义的异常不是RuntimeException。我的解决办法是,在注解@Transactional中添加rollbackFor={BizException.class}。可能你会问我为什么不将自定义异常修改为继承RuntimeException,因为我需要BizException是一个checked异常。
结束语:终于将spring事务中的异常回滚机制搞明白啦,欢迎读者在评论区添加其他导致spring事务不回滚的原因。
为什么不会滚呢??是对Spring的事务机制就不明白。!!
默认spring事务只在发生未被捕获的runtimeexcetpion时才回滚。
springaop异常捕获原理:被拦截的方法需显式抛出异常,并不能经任何处理,这样aop代理才能捕获到方法的异常,才能进行回滚,默认情况下aop只捕获runtimeexception的异常,但可以通过
<tx:methodname="upd*"propagation="REQUIRED"rollback-for="java.lang.Exception"/>
配置来捕获特定的异常并回滚
换句话说在service的方法中不使用trycatch或者在catch中最后加上thrownewruntimeexcetpion(),这样程序异常时才能被aop捕获进而回滚
解决方案:
方案1.例如service层处理事务,那么service中的方法中不做异常捕获,或者在catch语句中最后增加thrownewRuntimeException()语句,以便让aop捕获异常再去回滚,并且在service上层(webservice客户端,view层action)要继续捕获这个异常并处理
方案2.在service层方法的catch语句中增加:TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();语句,手动回滚,这样上层就无需去处理异常(现在项目的做法)
http://tech.lede.com/2017/08/15/rd/server/SpringSpringMVCContainerAndAOPCommonMistakes/
proxy-target-class="ture"
proxy-target-class属性值决定是基于接口的还是基于类的代理被创建。如果proxy-target-class属性值被设置为true,那么基于类的代理将起作用(需要CGLib库)。如果proxy-target-class属值被设置为false或者这个属性被省略,那么标准的JDK基于接口的代理将起作用。当然,即便未声明proxy-target-class=”true”,但运行类没有继承接口,Spring也会自动使用CGLib代理。
原理区别:Java动态代理是利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理。而CGLib动态代理是利用asm开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。使用情况如下:
如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现AOP
如果目标对象实现了接口,可以强制使用CGLib实现AOP
如果目标对象没有实现了接口,必须采用CGLib库,Spring会自动在JDK动态代理和CGLib之间转换
http://tech.lede.com/2017/02/06/rd/server/SpringTransactional/
所以,必须特别注意这些修饰符的使用,@Transactional注解只被应用到public可见度的方法上。如果你在protected、private或者package-visible的方法上使用@Transactional注解,它也不会报错,但是这个被注解的方法将不会展示已配置的事务设置。
spring事务拦截,要注意service里面的方法名称是否在事务配置里面
service的方法要以deleteinsertupdatesavedo开头事务才会生效
1.Service类内部方法调用
大概就是Service中有一个方法A,会内部调用方法B,方法A没有事务管理,方法B采用了声明式事务,通过在方法上声明Transactional的注解来做事务管理。
总结,在方法A中调用方法B,实际上是通过“this”的引用,也就是直接调用了目标类的方法,而非通过Spring上下文获得的代理类,所以事务是不会开启的。
2.try...catch异常
在一段业务逻辑中对数据库异常进行了处理,使用了try...catch子句捕获异常并throw了一个自定义异常,这种情况导致了事务未回滚,示例代码如下:
@Transactional(propagation=Propagation.REQUIRED,isolation=Isolation.READ_COMMITTED)
publicbooleanmethodB(Stringname)throwsBizException{
try{
rabbitDao.insertRabbit(name);
tortoiseDao.insertTortoise(name);
}catch(Exceptione){
thrownewBizException(ReturnCode.EXCEPTION.code,ReturnCode.EXCEPTION.msg);
}
returntrue;
}
BizException的定义如下:
publicclassBizExceptionextendsException{
//自定义异常
}
上面代码中的声明式事务在出现异常的时候,事务是不会回滚的。在代码中我虽然捕获了异常,但是同时我也抛出了异常,为什么事务未回滚呢?猜测是异常类型不对,于是开始查询原因,翻看了
17.5.3声明式事务的回滚
上一节中介绍了如何设置开启Spring事务,一般在你的应用的Service层代码中设置,这一节将介绍在简单流行的声明式事务中如何控制事务回滚。
在SpringFrameWork的事务框架中推荐的事务回滚方法是,在当前执行的事务上下文中抛出一个异常。如果异常未被处理,当抛出异常调用堆栈的时候,SpringFrameWork的事务框架代码将捕获任何未处理的异常,然后并决定是否将此事务标记为回滚。
在默认配置中,SpringFrameWork的事务框架代码只会将出现
注:UncheckedException包括Error与RuntimeException.RuntimeException的所有子类也都属于此类。另一类就是checkedException。
你可以精确的配置异常类型,指定此异常类事务回滚,包括checked异常。下面的xml代码片段展示了如何配置checked异常引起事务回滚,应用自定义异常类型:
<tx:adviceid="txAdvice"transaction-manager="txManager">与其有同等作用的注解形式如下:
<tx:attributes>
<tx:methodname="get*"read-only="true"rollback-for="NoProductInStockException"/>
<tx:methodname="*"/>
</tx:attributes>
</tx:advice>
@Transactional(rollbackForClassName={"NoProductInStockException"}) 或者 @Transactional(rollbackFor={NoProductInStockException.class})
在你遇到异常不想回滚事务的时候,同样的你也可指定不回滚的规则,下面的一个例子告诉你,即使遇到未处理的
<tx:adviceid="txAdvice">与其有同样作用的注解形式如下:
<tx:attributes>
<tx:methodname="updateStock"no-rollback-for="InstrumentNotFoundException"/>
<tx:methodname="*"/>
</tx:attributes>
</tx:advice>
@Transactional(noRollbackForClassName={"InstrumentNotFoundException"}) 或者 @Transactional(noRollbackFor={InstrumentNotFoundException.class})
还有更灵活的回滚规则配置方法,同时指定什么异常回滚,什么异常不回滚。当SpringFrameWork的事务框架捕获到一个异常的时候,会去匹配配置的回滚规则来决定是否标记回滚事务,使用匹配度最强的规则结果。因此,下面的配置例子表达的意思是,除了异常
<tx:adviceid="txAdvice">
<tx:attributes>
<tx:methodname="*"rollback-for="Throwable"no-rollback-for="InstrumentNotFoundException"/>
</tx:attributes>
</tx:advice>
你也可以通过编程式的方式回滚一个事务,尽管方法非常简单,但是也有非常强的代码侵入性,使你的业务代码和SpringFrameWork的事务框架代码紧密的绑定在一起,示例代码如下:
publicvoidresolvePosition(){如果可能的话,强烈推荐您使用声明式事务方式回滚事务,对于编程式事务,如果你强烈需要它,也是可以使用的,butitsusagefliesinthefaceofachievingacleanPOJO-basedarchitecture.(没懂...)
try{
//somebusinesslogic...
}catch(NoProductInStockExceptionex){
//triggerrollbackprogrammatically
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
}
}
看完官方文档这节内容找到了问题的答案,原来是因为我们自定义的异常不是
结束语:终于将spring事务中的异常回滚机制搞明白啦,欢迎读者在评论区添加其他导致spring事务不回滚的原因。
为什么不会滚呢??是对Spring的事务机制就不明白。!!
默认spring事务只在发生未被捕获的runtimeexcetpion时才回滚。
springaop异常捕获原理:被拦截的方法需显式抛出异常,并不能经任何处理,这样aop代理才能捕获到方法的异常,才能进行回滚,默认情况下aop只捕获runtimeexception的异常,但可以通过
<tx:methodname="upd*"propagation="REQUIRED"rollback-for="java.lang.Exception"/>
配置来捕获特定的异常并回滚
换句话说在service的方法中不使用trycatch或者在catch中最后加上thrownewruntimeexcetpion(),这样程序异常时才能被aop捕获进而回滚
解决方案:
方案1.例如service层处理事务,那么service中的方法中不做异常捕获,或者在catch语句中最后增加thrownewRuntimeException()语句,以便让aop捕获异常再去回滚,并且在service上层(webservice客户端,view层action)要继续捕获这个异常并处理
方案2.在service层方法的catch语句中增加:TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();语句,手动回滚,这样上层就无需去处理异常(现在项目的做法)
proxy-target-class属性值决定是基于接口的还是基于类的代理被创建。如果proxy-target-class属性值被设置为true,那么基于类的代理将起作用(需要CGLib库)。如果proxy-target-class属值被设置为false或者这个属性被省略,那么标准的JDK基于接口的代理将起作用。当然,即便未声明proxy-target-class=”true”,但运行类没有继承接口,Spring也会自动使用CGLib代理。
原理区别:Java动态代理是利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理。而CGLib动态代理是利用asm开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。使用情况如下:
如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现AOP
如果目标对象实现了接口,可以强制使用CGLib实现AOP
如果目标对象没有实现了接口,必须采用CGLib库,Spring会自动在JDK动态代理和CGLib之间转换
所以,必须特别注意这些修饰符的使用,@Transactional注解只被应用到public可见度的方法上。如果你在protected、private或者package-visible的方法上使用@Transactional注解,它也不会报错,但是这个被注解的方法将不会展示已配置的事务设置。
相关文章推荐
- spring 事务无法正常回滚的案例
- 关于Spring的@Transactional注解失效以及事务无法回滚问题
- Spring注解事务提交回滚
- Spring异常抛出触发事务回滚策略
- Spring中发生异常的事务回滚问题
- Spring transaction事务之roll back回滚: rollback-for
- spring事务异常回滚,捕获异常不抛出就不会回滚
- Spring事务超时、回滚的相关说明
- Spring事务回滚
- 异常捕获不抛出,Spring事务无法回滚
- Spring transaction事务之roll back回滚
- Spring事务异常回滚,捕获异常不抛出就不会回滚
- 实践中遇到Ibatis和spring整合事务不回滚的问题解决
- Spring中抛出异常时,既要要返回错误信息,还要做事务回滚
- Spring事务回滚和异常类
- Spring事务管理默认只对Unchecked Exception进行回滚
- 特定需求下动态代理导致的Spring事务不能回滚
- Spring事务回滚机制
- Spring回滚事务机制与创建数组的语法,Object[] params = new Object[]{值}
- Spring的事务管理对何种异常进行回滚