记一次Spring事务异常回滚小结

一、场景 

   由于最近所做的工作全部是接口对于数据库CURD操作,其中很多场景需要保证操作一致性。例如删除一个班级实体,需要先删除班级下的用户,再将用户班级关系存入历史,将班级信息存入历史,再将班级删除。需要保证数据的一致性,数据提交操作回滚至异常发生前的状态。

二、事务管理的目的

   再出现异常的情况下,保证数据的一致性;数据提交操作回滚至异常发生前的状态。

三、事物管理的方式

A、编程式事物管理:使用Transaction Template或PlatformTransactionManager实现。

B、声明式事务管理:建立在AOP之上,其本质是对方法前后进行拦截,然后在目标方法开始之前创建或者加入一个事物,在执行完目标方法之后更具执行情况提交或者回滚事物。(执行成功则提交,失败则进行实物的回滚)

编程式事务管理优势:可以控制事物的粒度,最细粒度到代码块级别。

声明式事物管理优势:在方法外进行声明,事物控制的代码不会与业务逻辑代码混在一起,最细粒度到方法级别;符合spring倡导的非入侵式的开发方式,即业务处理逻辑代码与事物管理代码不放在一起。

四、声明式事物管理实现方式

基于tx和aop名字空间的xml配置文件

// 基本配置




        
    



// MyBatis自动参与到spring事务管理中,无需额外配置,只要org.mybatis.spring.SqlSessionFactoryBean引用的数据源与DataSourceTransactionManager引用的数据源一致即可,否则事务管理会不起作用
// 标签的声明,则是在Spring内部启用@Transactional来进行事务管理,使用 @Transactional 前需要配置

A、基于@Transactional注解

@Transactional实质是使用了JDBC的事物来进行事物控制的

@Transactional注解是基于Spring的动态代理机制。

B、Transactional实现原理

事务开始时,通过AOP机制,生成一个代理connection对象,并将其放入DataSource实例的某个与DataSourceTransactionManager相关的某处容器中。在接下来的整个事务中,客户代码都应该使用该connection连接数据库,执行所有的数据库命令【不使用该connection连接数据库执行的数据库命令,在本事物回滚的时候得不到回滚】(物理连接connection逻辑上新建一个会话session;DataSource与TransactionManager配置相同的数据源)

事务结束时,回滚在第1步骤中得到的代理connection对象上执行的数据库命令,然后关闭该代理connection对象(事务结束后,回滚操作不会对已执行完毕的SQL操作命令起作用)

五、Transactional配置属性

属性 类型 描述
value String 可选的限定描述符,指定使用的事务管理器
propagation enum:Propagation 可选的事务传播行为设置
isolation enum:Isolation 可选的事务隔离级别设置
readOnly boolean 读写或只读事务,默认读写
timeout int(in seconds granularity) 事务超时时间设置
rollbackFor Class对象数组,必须继承自Throwable 导致事务回滚的异常类数组
rollbackForClassName 类名数组,必须继承自Throwable 导致事务回滚的异常类名字数组
noRollbackFor Class对象数组,必须继承自Throwable 不会导致事务回滚的异常类数组
noRollbackForClassName 类名数组,必须继承自Throwable 不会导致事务回滚的异常类名字数组

说明

value:主要用来指定不同的事物管理器;主要用来满足在同一个系统中,存在不同的事物管理器。比如在spring中,声明了两种事物管理器txManager1,txManager2然后,用户可以根据这个参数来根据需要指定特定的txManager

value 适用场景:在一个系统中,需要访问多个数据源或者多个数据库,则必然会配置多个事物管理器的。

rollbackFor:让受检查异常回滚;即让本来不应该回滚的进行回滚操作。

noRollbackFor:忽略非检查异常;既让本来应该回滚的不进行回滚操作。

六、spring事物回滚规则及配置

spring事物回滚规则:

指示spring事物管理器回滚一个事物的推荐方法是在当前事务的上下文内抛出异常,spring事务管理器会捕捉任何未处理的异常,然后依据规则决定是否回滚抛出异常的事务。

默认配置下,spring只有在抛出的异常为运行时unchecked异常时才回滚该事务,也就是抛出的异常为RuntimeException的子类,而抛出checked异常则不会导致事务回滚。

用spring事务管理器,由spring来负责数据库的打开,提交,回滚,默认遇到运行期例外(throw new RuntimeException("注释");)会回滚,即遇到不受检查(unchecked)的例外时回滚;而遇到需要捕获的例外(throw new Exception("注释"))不会回滚,即遇到受检查的例外(就是非运行时抛出的异常,编译器会检查到的异常叫受检查例外或说受检查异常)时,需要我们指定方式来让事务回滚要想所有的异常都回滚,要加上@Transactional(rollbackFor={Exception.calss,其它异常}),如果让unchecked例外不回滚:@Transactional(notRollbackFor=RunTimeException.class)

注意事项:

· @Transactional 使用位置 类上方、方法上方

Spring建议不要在接口或者接口方法上使用该注解,因为这只有在使用基于接口的代理时它才会生效当作用于类上时,该类的所有public方法都将具有该类型的事务属性,同时,我们也可以在方法级别使用该标注来覆盖类级别的定义

· 方法的访问权限为public

@Transactional注解应该只被应用到public方法上,这是由Spring AOP 的本质决定的,在 protected、private 或者默认可见性的方法上使用@Transactional注解,这将被忽略,也不会抛出任何异常

· 默认情况下,只有来自外部的方法调用才会被AOP代理捕获,也就是,类内部方法调用本类内部的其他方法并不会引起事务行为,即使被调用方法使用@Transactional注解进行修饰

例如一:同一个类中方法,A方法未使用此标签,B使用了,C未使用,A调用B,B调用C;则外部调用A之后,B的事务是不会起作用的。

例如二:若是有上层(按照Controller层,Service层,DAO层的顺序)由Action 调用 Service 直接调用,发生异常会发生回滚;若间接调用,Action 调用 Service 中的A方法,A无@Transactional注解,B有, A调用B,B的注解无效

非RuntimeException回滚解决方案:

方案1、例如service层处理事务,那么service中的方法不做异常捕获,或者在catch语句中最后增加 throw new RuntimeException()语句,以便让aop捕获异常再去回滚,并且在service上层(webservice客户端,view层action)要继续捕获这个异常并处理。

方案二、在service层方法的catch语句中增加:TransactionAspectSupport.currentTransactionSatus().setRollbackOnly();语句,手动回滚,这样上层就无需去处理异常

方案三、我们指定方式来让事务回滚要想所有的异常都回滚,要加上@Transactional(rollbackFor={Exception.calss})


参考文献

https://blog.csdn.net/mingyundezuoan/article/details/79017659
https://blog.csdn.net/yipanbo/article/details/46048413
https://blog.csdn.net/bjweimengshu/article/details/79607522

你可能感兴趣的:(Java)