Spring事务管理不能回滚问题

在前段时间学习SpringMVC的练习中,碰到声明式事务管理时,事务不能回滚的情况,通过查看博客和资料,解决了问题。

原因

导致Spring事务管理不能回滚的原因有两个:一是Service内部方法调用,二是使用了try...catch异常。

Service内部方法调用

现在我的Service层中,定义一个转账的功能给它加上事务,而另一个方法直接调用并没有事务。

@Service
public class YHService{
// 开启事务
@Transactional(isolation = Isolation.DEFAULT, propagation = Propagation.REQUIRED)
public void transferDemo1(){
// 转账逻辑代码。。。
int i = 1/0;
} // 未开启事务
public void transferDemo2(){
transferDemo1();
}
}

现在编写Test


public class YHServiceTest{
@Autowired
private YHService service; @Test
public void testDemo1(){
service.transferDemo1();
} @Test
public void testDemo2(){
service.transferDemo2();
}
}

结果就是调用testDemo1时,事务发生了回滚,而调用testDemo2时事务并没有回滚,这是为什么?

原来使用声明式事务来进行管理时,是通过AOP动态产生代理类(serviceProxy)来进行事务管理,而目标类(service)是没有事务管理的,所以在service内部(this)调用的方法是不能被serviceProxy事务管理所负责,可能会好奇我的testDemo2不就是通过serviceProxy调用的吗?为什么transferDemo2能进行事务管理而transferDemo1却不能?看看下面serviceProxy事务管理运行的原理把。



看完之后就清楚了,我们使用Spring的事务管理,首先是调用serviceProxy,再调用service的目标方法退出方法时提交/回滚。当我们调用transferDemo2时,此时的方法加入了serviceProxy的事务管理,而transferDemo2内通过了this调用了transferDemo1而此时尽管用注解/xml的形式声明了事务管理,但是却是通过service来调用该方法的,并不是serviceProxy调用,所以他保持的是和transferDemo2一样的事务,此处即为没有事务。

简而言之就是:目标对象内部的自我调用将无法实施切面中的增强,如果要实现,就只能通过代理类去调用该目标方法即可达到目的。代码变现就是将this调用换成serviceProxy调用就可以了,可以用注入方式达成目的,很多种方法,可以自行查资料。

try...catch...

在刚刚开始学习Spring事务的时候我们并没有去给逻辑代码加上try...catch..直接让程序异常回滚,但是也不能一直这样,有些情况下我们需要处理异常就要用try...catch...去处理异常,所以我们接下来看看再try...catch...中程序异常,事务是否能回滚

mapper层:

public interface DaoMapper {
// 转出
@Update("update t_user set balance=balance-#{money} where id=#{id}")
void push(@Param("id") int id, @Param("money") double money);
// 转入
@Update("update t_user set balance=balance+#{money} where id=#{id}")
void pull(@Param("id")int id, @Param("money")double money);
}

Service:

@Service
public class UserServerImpl implements UserServer {
private DaoMapper mapper; @Autowired
public void setMapper(DaoMapper mapper) {
this.mapper = mapper;
} @Override
public void transferAccounts(int pushId, double money, int pullId) {
try {
mapper.push(pushId, money);
int i = 7 / 0;
mapper.pull(pullId, money);
}catch (Exception e){
System.out.println("程序异常,事务回滚");
}
}
}

上面的代码我们对其中的异常进行了捕获并处理,运行发现事务并没有进行回滚,这是为什么,猜测应该是跟try...catch...有关,查阅资料博客,发现我们如果对异常进行了手动捕获处理,并没有throw出异常这样导致了Spring的事务管理拦截不到异常,就不会对事务进行回滚,OK那么我们加上throw e;运行程序,结果正常,事务也回滚了。

奇怪的是我抛出的是Exception类型的异常,也没有在xml中的事务管理进行rollback-for配置,为啥会回滚呢?

在默认配置中,Spring FrameWork 的事务框架代码只会将出现runtime, unchecked 异常的事务标记为回滚;也就是说事务中抛出的异常时RuntimeException或者是其子类,这样事务才会回滚(默认情况下Error也会导致事务回滚)。在默认配置的情况下,所有的 checked 异常都不会引起事务回滚。

按理来说Spring不应该回滚,后面我将改成了throw new Exception(),运行程序发现事务没有回滚,改成throw new RuntimeException(),运行程序发现事务回滚了。

好像和throw的对象有关,之前我们throw出的是我们当前捕获的异常对象,发现被Spring事务管理回滚了,后面我们throw出的是新的Exception实例对象,没有被Spring事务管理拿到,而throw出的是新的RuntimeException实例对象却被Spring事务管理拿到了,当我在xml文件中配置了rollback-for="Exception",发现throw出新的Exception实例对象也能被捕获到并回滚。我好奇的是符合上诉的异常回滚规则的竟然是throw出新的异常类型实例对象。而不是catch中的对象实例,这是为什么?有没有大神解惑一下,谢谢!

最新文章

  1. HSLA颜色
  2. 实现tip浮层
  3. Linux串口设置及编程(转)
  4. 我对javascript的自以为是
  5. Web服务器性能/压力测试工具http_load、webbench、ab、Siege使用教程 - VPS侦探
  6. express 安装与卸载
  7. <php>json小结
  8. 查询SQL中字符串里有多少个逗号
  9. shell程序之逐行读取一文件里的參数且使用此參数每次运行5分钟
  10. 根据实践经验,讲述些学习Java web能少走的弯路,内容摘自java web轻量级开发面试教程
  11. [AHOI 2012]树屋阶梯
  12. centos7设置httpd
  13. Bootstrap常用单词组
  14. 在.NET程序中实现HttpServer功能
  15. PDF文件转换成Excel表格的操作技巧
  16. Intersection of Two Linked Lists(LIST-2 POINTER)
  17. Activiti6.0 java spring5 SSM 工作流引擎 审批流程 项目框架
  18. java对对象排序
  19. OpenERP 负载平衡
  20. 动态规划 HDU1231-------最大连续子序列

热门文章

  1. springboot整合ehcache缓存失效
  2. Podman 快速入门
  3. Docker介绍及安装详解
  4. CF474D Flowers 题解
  5. MySQL的函数使用
  6. 谷歌内部流出Jetpack Compose最全上手指南,含项目实战演练!
  7. Alibaba-技术专区-Dubbo3总体技术体系介绍及技术指南(序章)
  8. JCE加密和解密 bouncycastle
  9. Kafka丢数据、重复消费、顺序消费的问题
  10. Centos7上yum安装mongodb4-2