Spring声明式事务的使用

Spring声明式事务的使用

1 从@Transactional注解开始 #

@Transactional注解是使用spring-transaction的最便捷方式。也是使用Spring框架开发最先接触的内容。

当你在一个服务的方法上使用@Transactional注解时,意味着你希望为此方法开启事务支持。如果你的项目成功配置了数据源事务管理器,Spring会为此方法使用如下默认设置开启事务:

  • 事务传播属性:PROPAGATION.REQUIRED
  • 事务隔离级别:ISOLATION_DEFAULT (使用数据库的默认隔离级别)
  • 只读事务:否
  • 事务超时时间取决与数据库的事务超时设置(MYSQL innodb默认为50s),若数据库不支持事务超时则不设置
  • 事务回滚:RuntimeException时回回滚(unchecked exception); Error和CheckedException(如IOException等)时不回滚

像这样,不做任何其他的配置,就可轻松使用事务控制。这是Spring给Java开发带来的便利,对于业务简单的场景,简单的@Transactional注解即可满足业务需求——完全使用数据库的事务来处理业务。

使用@Transactional注解之前,需要配置相关TransactionManager; 对于Spring-Boot项目,可以使用@EnableTransactionManagement注解,该注解的作用和<tx: annotation-driven/>相当,其为Spring事务控制开启注解支持。

1.1 普通方法调用@Transactional方法 #

声明式事务的开启都是由@Transactional注解开始的,事务的开始必然是从普通方法到@Transactional方法的调用。因此,形如如下的方法调用,事务必然生效:

1public void A() throws Exception{
2    service.B();
3}
4
5@Transactional
6public void B() throws Exception{
7    // ...
8}

上述代码片段中,方法B的事务会生效,并且和方法A方法没有关系;而方法A是不受事务控制的

这种行为较容易理解, @Transactional注解的作用范围只在注解的方法内生效。

1.2 @Transactional方法调用普通方法 #

如果反过来,当@Transactional方法调用普通方法,会有怎样的事务行为呢?

1@Transactional
2public void A() throws Exception{
3    B();
4}
5public void B() throws Exception{
6    // ...
7}

实际上,此种情形只有A方法存在事务,B方法没有事务,不过,有一些行为表现得好像方法B 使用了方法A的事务一样:

此种情形下,方法B实际上被方法A的事务所管理:

  1. B抛出异常时:
    • 若方法A捕获异常,则方法A和方法B均提交
    • 若方法A不捕获异常,则方法A和方法B均回滚
  2. 当方法A抛出异常时,方法A和方法B均回滚

总之,若方法A捕获了方法B抛出异常,则事务提交;若方法A没有捕获方法B抛出的异常,则认为方法A抛出异常,则事务回滚。

A方法抛出异常,则事务回滚,值得一题的是,这种情形下,B方法若执行,也会回滚

2 理解Spring声明式事务 #

仅仅依靠@EnableTransactionManagement@Transactional注解来使用Spring声明式事务显然是不够的,你必须理解其实现原理,才能有效配置使其按照预期工作。Spring事务实际上是通过Spring AOP代理来对目标类/方法添加环绕通知增强来实现的。事务通知(transaction advice)通过XML或者注解的形式配置。

借助AOP代理以及元数据(XML或注解)配置,Spring会使用合适的TransactionManager以及TransactionInterceptor来对调用方法增强。

Spring 事务支持2种编程模型:强制性(imperative programming model)和响应式(reactive programming model),目前前者使用的比较多。

强制性事务使用PlatformTransactionManager,这是常见且应熟识的;而响应式事务使用ReactiveTransactionManager

@Transactional注解的作用域为当前执行线程,即当前方法及其调用方法中发生的数据访问和操作,均被PlatformTransactionManager所提供的事务所管理。不过,对于方法内部新启动的线程,事务不会生效

下图粗略展示了调用使用@Transactional注解的方法时,事务代理的作用方式:

事务代理作用示意图

3 配置@Transactional注解 #

@Transactional 注解能够在接口方法上使用。

当在类上使用@Transactional注解时,该类所有的方法以及其子类的方法都会被Spring事务管理。

在方法上使用@Transactional注解时,意味着为这个方法开启Spring事务,这是最常用的方式。需要注意的是,如果在非public方法上使用@Transactional注解,Spring不会抛出任何异常,但是Spring事务不会生效。

在接口或接口方法上使用@Transactional注解,Spring事务只有在使用基于接口的代理时才会生效。所以当你在使用基于