Spring事务
事务传播行为
Spring中的事务传播行为:
- 支持当前事务
REQUIRED
:支持当前事务,如果当前不存在则新开启一个事务(默认配置)。SUPPORTS
:支持当前事务,如果当前不存在事务则以非事务方式执行。MANDATORY
:支持当前事务,如果当前不存在事务则抛出异常。
- 不支持当前事务
REQUIRES_NEW
:创建一个新事务,如果当前已存在事务则挂起当前事务。NOT_SUPPORTED
:以非事务方式执行,如果当前已存在事务则挂起当前事务。NEVER
:以非事务方式执行,如果当前已存在事务则抛出异常。
- 嵌套事务
NESTED
:如果当前存在事务,则在嵌套事务中执行,否则开启一个新事务。
NESTED
和REQUIRES_NEW
的区别:
REQUIRES_NEW
是新建一个事务并且新开始的这个事务与原有事务无关,而NESTED
则是当前存在事务时会开启一个嵌套事务,在NESTED
情况下,父事务回滚时,子事务也会回滚,而REQUIRES_NEW
情况下,原有事务回滚,不会影响新开启的事务!
NESTED
和REQUIRED
的区别:
REQUIRED
情况下,调用方存在事务时,则被调用方和调用方使用同一个事务,那么被调用方出现异常时,由于共用一个事务,所以无论是否catch异常,事务都会回滚,而在NESTED
情况下,被调用方发生异常时,调用方可以catch其异常,这样只有子事务回滚,父事务不会回滚。
事务传播行为(propagation behavior)指的就是当一个事务方法被另一个事务方法调用时,这个事务方法应该如何进行。正常来说有几种解决方案:
- 融入事务:直接去掉serviceB中关于开启事务和提交事务的begin和commit,融入到serviceA的事务中。问题:B事务的错误会引起A事务的回滚。
- 挂起事务:如果不想B事务的错误引起A事务的回滚,可以开启两个连接,一个执行A一个执行B,互不影响,执行到B的时候把A挂起新起连接去执行B,B执行完了再唤醒A执行。
- 嵌套事务:MySQL中可以通过给B事务加savepoint和rollback去模拟嵌套事务,把B设置成伪事务。
参考:
Spring事务失效场景
1、方法不是public
的。如果我们自定义的事务方法的访问权限不是public,spring则不会提供事务功能。
2、方法用final
修饰。如果某个方法用final修饰了,那么在它的代理类中,就无法重写该方法添加事务功能。
3、方法内部调用。方法拥有事务的能力是因为spring aop生成代理了对象,但是方法内部调用直接调用了this对象的方法,所以不会生成事务。解决方案:
- 新加一个Service方法
- 在该Service类中注入自己
- 通过
AopContent
获取当前类的代理类。在引导类上添加@EnableAspectJAutoProxy(exposeProxy=true)
注解,在该Service类中使用AopContext.currentProxy()
获取代理对象。 - 通过
ApplicationContext
引入Bean。即applicationContext.getBean
。
4、未被spring管理。如没有添加@Service
注解。
5、在另一个线程中使用事务。Spring事务管理的方式就是通过ThreadLocal
把数据库连接与当前线程绑定,如果在不同的线程,拿到的数据库连接肯定是不一样的,所以是不同的事务。
6、表不支持事务。如MyISAM
引擎。
7、未开启事务。 如:未启用事务管理(Springboot启动类或配置类要添加@EnableTransactionManagement
注解)、显式配置的事务切点不合适。
8、错误的传播行为。只有三种传播行为才会创建新事务:REQUIRED
,REQUIRES_NEW
,NESTED
。
9、异常未抛出。如果想要spring事务能够正常回滚,必须抛出它能够处理的异常。如果没有抛异常,则spring认为程序是正常的。
10、手动抛了别的异常。spring事务默认情况下只会回滚RuntimeException
(运行时异常)和Error
(错误),默认不回滚Exception
(非运行时异常)。
11、自定义了回滚异常,但实际抛出别的异常。建议将rollbackFor
设置为Exception.class
。
12、嵌套事务回滚多了。嵌套事务中,如果想内部事务回滚而不影响外部事务,需要在外部事务中捕获内部事务的异常。
13、大事务问题。如果一个事务在更新一个大表时使用了行级锁,并且更新的数据量很大,其他事务可能会被阻塞,无法访问或更新相同的数据。如果系统资源被大事务长时间占用,其他事务可能无法及时得到资源,导致事务队列积压,影响系统的响应性能和事务的执行效率。
参考:
Spring事务实现原理
在使用Spring框架的时候,可以有两种事务的实现方式,一种是编程式事务,有用户自己通过代码来控制事务的处理逻辑,还有一种 是声明式事务,通过@Transactional
注解来实现。
其实事务的操作本来应该是由数据库来进行控制,但是为了方便用户进行业务逻辑的操作,spring对事务功能进行了扩展实现,一般我们很少会用编程式事务,更多的是通过添加@Transactional
注解来进行实现,当添加此注解之后事务的自动功能就会关闭,有spring框架来帮助进行控制。
其实事务操作是AOP的一个核心体现,当一个方法添加@Transactional
注解之后,spring会基于这个类生成一个代理对象,会将这个代理对象作为bean,当使用这个代理对象的方法的时候,如果有事务处理,那么会先把事务的自动提交给关闭,然后去执行具体的业务逻辑,如果执行逻辑没有出现异常,那么代理逻辑就会直接提交。如果出现任何异常情况,那么直接进行回滚操作,当然用户可以控制对哪些异常进行回滚操作。