Spring声明式事务异常回滚机制
前言
Spring事务管理是根据异常来进行回滚操作;
Spring与Mybatis整合时,虽然在Service方法中并没有checked异常,但是如果数据库有异常发生,默认会进行事务回滚。
Spring如果不添加rollbackFor等属性,Spring碰到Unchecked Exceptions都会回滚,不仅仅是RuntimeException,也包括Error。
如果在事务方法中捕获异常并进行处理,一定要继续抛出异常并在Spring事务管理中进行rollbak-for配置。
概述
首先看一个Service层的方法,该方法调用两个DAO层的方法分别往数据库中的两张表中插入数据,实现一个业务逻辑。而且根据业务规则,这两个插入动作是要进行事务控制的原子操作,即要么一起成功,要么一起失败,否则会导致数据一致性问题。
1 |
|
说明:若在这个Service层的方法上不加任何事务注解,则事务的边界为DAO层的方法。即当程序执行完第一个DAO方法的调用后,事务已经被提交了。(可以通过pl/sql developer工具连接到数据库后,发现此时记录已经被插入到表中了来验证)。因此,第二句的DAO层的方法如果一旦执行错误,已经无法将上一句DAO层方法以及插入到数据库中的数据进行回滚,导致数据一致性问题。
Spring通过@Transactional注解实现声明式事务。一般事务的边界定义在Service层的方法中。
1 |
|
说明:若如上述代码一样,只加了一个默认的@Transactional标注,而不加任何标注参数。则此时,当第一个DAO层方法执行完成后,事务没有被提交(可以通过pl/sql developer工具连接到数据库后,发现此时记录没有被插入到表中来验证)。当程序执完整个Service方法,且上述两句DAO层方法都执行成功,则事务才被提交(可以通过pl/sql developer工具连接到数据库后,发现此时两条记录都已经被插入到表中来验证)。
若第二句DAO层方法执行失败,Spring会根据异常类型来做不同的处理,即:若执行失败,若抛出的异常为RuntimeExcepition类型或其子型的异常对象,则上句DAO层语句执行的结果会被回滚。否则,上句执行的结果会被提交。这样也会导致业务原子性被破坏,导致数据不一致的情况。
为了保证在Service层的方法中,无论遇到任何类型的异常,都让事务进行回滚,而不是让之前已经执行成功的语句提交,则必须在@Transactional注解中增加如下参数rollbackFor:
1 |
|
说明:由于Exception是所有异常类的超类,这样对于任何异常,Spring都会进行回滚,而不会将引发异常之前已经执行成功的提交掉。
对于那些我们在代码中主动抛出的异常,我们可以将rollbackFor参数指定为这些特定异常,即遇到该异常,就进行回滚操作。
1 |
|
说明:上述Service方法作为一个原子业务逻辑,实现了两个操作,一个是调用DAO层方法往数据库里面插入数据,另一个是调用另一个系统的Webservice接口,将数据推送过去。若第一个DAO层方法执行成功,而第二个操作由于网络等原因导致调用Webservice接口失败,则程序会主动抛出ArcpipeException异常,而由于事务注解中已经设置了参数rollbackFor=ArcpipeException.class,因此该事务会被回滚掉。
或者我们在自定义异常的时候,让自定义的异常继承自RuntimeException,这样抛出的时候才会被Spring默认的事务处理。