一、try catch 对 Spring 事务的影响
当 try catch 捕获了异常,事务不会回滚。如果非得在 service 层写 try catch,需要 catch 后 throw new RuntimeException 让事务回滚。如果使用 try catch 捕获抛出的unchecked exception
后没有在 catch 块中采用页面硬编码的方式使用 Spring api 对事务做显式的回滚,则事务不会回滚。将异常捕获,而未在 catch 块中对事务做显式回滚等同于生吞掉异常。
1️⃣catch 只打印异常,不抛出异常
try {
数据库新增订单逻辑;
int a=5/0;
数据库减少库存逻辑;
}catch (Exception e){
e.printStackTrace();
}
该方法会影响事务,此时数据库中订单数据会插入成功!因为 Spring 的事务的标准是 RuntimeException。
2️⃣catch 打印异常,并抛出异常
try {
数据库新增订单逻辑;
int a=5/0;
数据库减少库存逻辑;
}catch (Exception e){
e.printStackTrace();
throw new RuntimeException();
}
该方法不会影响事务,因为抛出了 RuntimeException。
二、Spring 事务的默认机制
1️⃣不可查的异常(unchecked exceptions):RuntimeException 及其子类和错误(Error)
~~~回滚
2️⃣可查的异常(checked exceptions):Exception 下除 RuntimeException 之外的异常。即需要 try catch 或 向上抛出的 异常
~~~不回滚
3️⃣可以配置所有异常回滚:@Transactional(rollbackFor = {Exception.class})
说明:
Spring 的 AOP 即声明式事务管理,默认是针对unchecked exception
回滚,也就是默认对RuntimeException()
或是其子类进行事务回滚。
要想捕获非运行时异常则需要如下配置:
1️⃣在针对事务的类中抛出 RuntimeException,而不是抛出 Exception。
2️⃣在 txAdive 中增加 rollback-for,里面写自己的 exception:
<tx:advice id='txAdvice' transaction-manager='transactionManager'>
<tx:attributes>
<tx:method name='*' rollback-for='com.cn.untils.exception.XyzException'/>
</tx:attributes>
</tx:advice>
或者定义不回滚的异常:
<tx:advice id='txAdvice'>
<tx:attributes>
<tx:method name='update*' no-rollback-for='IOException'/>
<tx:method name='*'/>
</tx:attributes>
</tx:advice>
在 @Transactional 中如果不配置 rollbackFor 属性,那么事务只会在遇到 RuntimeException 的时候才会回滚。加上 rollbackFor=Exception.class,可以让事务在遇到非运行时异常也回滚。
如果不对运行时异常进行处理,那么出现运行时异常之后,要么是线程中止,要么是主程序终止。 如果不想终止,则必须捕获所有的运行时异常,决不让这个处理线程退出。队列里面出现异常数据了,正常的处理应该是把异常数据舍弃,然后记录日志。不应该由于异常数据而影响下面对正常数据的处理。
非运行时异常是 RuntimeException 以外的异常,类型上都属于 Exception 类及其子类。如 IOException、SQLException 等以及自定义的 Exception。对于这种异常,Java 编译器强制要求必须对出现的这些异常进行 catch 并处理,否则程序就不能编译通过。所以,面对这种异常不管是否愿意,必须写一大堆 catch 块去处理可能的异常。三、Spring 的事务默认取决于是否抛出 RuntimeException
Spring 的事务边界是在调用业务方法之前开始的,业务方法执行完毕之后来执行 commit or rollback。如果抛出 RuntimeException 并在业务方法中没有 catch 到的话,事务会回滚。一般不需要在业务方法中 catch 异常,如果非要 catch,在做完想做的工作后(比如关闭文件等)一定要抛出 RuntimeException,否则 Spring 会将操作 commit,这样就会产生脏数据。所以这样的 catch 代码是画蛇添足。如:
try {
//bisiness logic code
} catch(Exception e) {
//handle the exception
}
由此,在 Spring 中如果某个业务方法被 try catch 整个包裹起来,则这个业务方法也就等于脱离了 Spring 事务的管理,因为没有任何异常会从业务方法中抛出!全被捕获并吞掉,导致 Spring 异常抛出触发事务回滚策略失效。不过,如果在 catch 代码块中采用页面硬编码的方式使用 Spring api 对事务做显式的回滚,这样也未尝不可。