Spring-项目中Spring 声明式事务使用的一些坑点分析02
基于上次对""分析后感觉还是有一定收获的,现在开始进行对Spring 事务进行更进一步的分析坑点,我之前在开发的时候有人好像这样告诉过我"只要在同一个service方法中有多个操作是对数据表进行更改(insert,update,delete)都需要在该方法上加上@Transactional(rollbackFor = Exception.class)",今天我就来分析一下在指定情况下其实不用加这个注解其实也是能回滚数据的(只是Spring事务配置方式的不同),注意如下的案例采用的Spring事务管理是:基于tx和aop名字空间的xml配置文件,详细请查考""。
1. 案例1:
@Overridepublic void handleNoTransactionAnnotation(){ SecurityAddition record = new SecurityAddition(); record.setAmt(new Double(20)); record.setCard(System.currentTimeMillis()+""); record.setOrder_no("Order-"+System.currentTimeMillis()); record.setName("张三"); securityAdditionMapper.insert(record); throw new RuntimeException("抛出异常了...");}
如上代码是SecurityAddition类对应security_addition表,对于security_addition表的插入后抛出了RuntimeException,分析:在项目配置文件中其实配置对应service方法的事务传播行为:<tx:method name="handle*" propagation="REQUIRED" />,所以当代码执行到handleNoTransactionAnnotation这个方法的时候就会判断:如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。这里是没有事务创建一个事务,这个方法内部的代码执行完后就会提交事务,所以这里代码没有执行完就抛出RuntimeException所以security_addition表中是没有插入数据的,因为Spring 发现出错了,它会回滚,其实就是不提交目前这个手动事务。
2. 案例2:
@Overridepublic void handleNoTransactionAnnotation() { SecurityAddition record = new SecurityAddition(); record.setAmt(new Double(20)); record.setCard(System.currentTimeMillis()+""); record.setOrder_no("Order-"+System.currentTimeMillis()); record.setName("张三"); securityAdditionMapper.insert(record); Admin admin = new Admin(); admin.setCreateTime(new Date()); admin.setId(28); admin.setDoctorId(101); admin.setHospitalId(101); admin.setPassword("123456"); admin.setPayName("张三"); admin.setState(1); admin.setToken("88888888"); admin.setAccount("zhangsan"); admin.setType("USER"); adminMapper.insert(admin); throw new RuntimeException("抛出异常了...");}
那么对于多表操作或者多个同表变更操作,这个时候如上代码还能正常回滚吗? 答案是:可以的,原因:他们二个表都是在同一个数据源下的,这里方法内部是使用的一个事务,而不是一个操作一个事务,所以这里即使在代码最后抛出RuntimeException(这里包括RuntimeException的所有子类异常)都是能将当前同一事务下的多个操作进行回滚的。
3. 案例3:
@Overridepublic void handleNoTransactionAnnotation() throws Exception{ SecurityAddition record = new SecurityAddition(); record.setAmt(new Double(20)); record.setCard(System.currentTimeMillis()+""); record.setOrder_no("Order-"+System.currentTimeMillis()); record.setName("张三"); securityAdditionMapper.insert(record); Admin admin = new Admin(); admin.setCreateTime(new Date()); admin.setId(28); admin.setDoctorId(101); admin.setHospitalId(101); admin.setPassword("123456"); admin.setPayName("张三"); admin.setState(1); admin.setToken("88888888"); admin.setAccount("zhangsan"); admin.setType("USER"); adminMapper.insert(admin); // ** 注意这里异常变了 ** throw new Exception("抛出异常了...");}
这里我将RuntimeException异常改为了Exception异常了,这个时候能正常回滚吗?答案是:不能,因为 Spring 中回滚是默认RunTimeException才会回滚,如果抛出的不是RunTimeException,Spring是默认不会回滚的。
所以综合上述案例效果可以得出结论:只要在对应service方法中只要你能保证抛出的异常都是RuntimeException异常那就可以不用加@Transactional(rollbackFor = Exception.class)这个注解。那我们什么时候才会用到这个注解了,就是当我们需要对service方法中抛出Exception也能回滚事务的操作下,我们就可以使用到这个注解。
总结:本篇文章+"",其实我们可以对上面的说法进行一定的了解了,对于这种说法:"只要在同一个service方法中有多个操作是对数据表进行更改(insert,update,delete)都需要在该方法上加上@Transactional(rollbackFor = Exception.class)",这种说法其实是对的(只要在配置文件中配置方式是基于@Transactional的事务配置),@Transactional(rollbackFor = Exception.class)是基于@Transactional的显示事务控制方式,所有只要涉及到数据表进行更改(insert,update,delete)的操作为了保证事务的ACID就需要这么做。由于Spring声明式事务配置方式的不同,所以我们在使用的时候一定要先区分项目中采用的是那种方式,然后再去合理的使用,不要再使用"基于tx和aop名字空间的xml配置文件"方式中去使用@Transactional注解,这样导致的后果就是注解根本就没有生效。
异常相关知识可以参考博客: