SpringBoot声明式事务的简单应用

SpringBoot声明式事务的简单应用

九月 25, 2019

Spring的事务机制

所有的数据访问技术都有事务处理机制,这些技术提供了API用来开启事务、提交事务来完成数据操作,或者在发生错误的时候回滚数据。
而Spring的事务机制是用统一的机制来处理不同数据访问技术的事务处理。Spring的事务机制提供了一个PlatformTransactionManager接口,不同的数据访问技术的事务使用不同的接口实现:

具体的PlatformTransactionManager继承关系如下:

声明式事务

Spring声明式事务的实现,有两种方式;第一种是配置xml,第二种是使用相关注解。SpringBoot中默认配置了第二种方式,所以,SpringBoot直接使用注解即可。下面介绍SpringBoot通过注解开启事务的使用。

SpringBoot使用事务的步骤:

  1. 在pom.xml添加依赖

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    <dependencies>
    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    </dependency>
    <dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    </dependency>
    <!--get,set插件-->
    <dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    </dependency>
    </dependencies>
  2. 启动类上开启事务支持

    1
    2
    3
    4
    5
    6
    7
    8
    9
    @SpringBootApplication
    @EnableTransactionManagement
    public class SprintbootTramsaction
    {
    public static void main( String[] args )
    {
    SpringApplication.run(SprintbootTramsaction.class,args);
    }
    }

备注:@EnableTransactionManagement注解其实在大多数情况下,不是必须的,因为SpringBoot在
TransactionAutoConfiguration类里为我们自动配置启用了@EnableTransactionManagement注
解。不过自动启用该注解有两个前提条件,分别是:
@ConditionalOnBean(PlatformTransactionManager.class)
和@ConditionalOnMissingBean(AbstractTransactionManagementConfiguration.class),而一
般情况下,这两个条件都是满足的,所以一般的,我们在启动类上写不写
@EnableTransactionManagement都行。

  1. 在业务逻辑层接口实现的相关方法添加事务声明
    1
    2
    3
    4
    @Transactional
    public void saveSomething(Long id, String name) {
    //数据库操作
    }

Transactional注解的常用属性表:

属性 说明
propagation 事务的传播行为,默认值为 REQUIRED。
isolation 事务的隔离度,默认值采用 DEFAULT
timeout 事务的超时时间,默认值为-1,不超时。如果设置了超时时间(单位秒),那么如果超过该时间限制了但事务还没有完成,则自动回滚事务。
read-only 指定事务是否为只读事务,默认值为 false;为了忽略那些不需要事务的方法,比如读取数据,可以设置 read-only 为 true。
rollbackFor 用于指定能够触发事务回滚的异常类型,如果有多个异常类型需要指定,各类型之间可以通过逗号分隔。{xxx1.class, xxx2.class,……}
noRollbackFor 抛出 no-rollback-for 指定的异常类型,不回滚事务。{xxx1.class, xxx2.class,……}

……

备注:

  • service实现类(一般不建议在接口上)上添加@Transactional,可以将整个类纳入spring事务管理,在每个业务方法执行时都会开启一个事务,不过这些事务采用相同的管理方式。
  • Transactional 注解只能应用到 public 可见度的方法上。 如果应用在protected、private或者 package可见度的方法上,也不会报错,不过事务设置不会起作用。
  • 默认情况下,Transactional 注解的事物所管理的方法中,如果方法抛出运行时异常或error,那么会进行事务回滚;如果方法抛出的是非运行时异常,那么不会回滚。
  • SQL异常属于检查异常(有的框架将SQL异常重写为了运行时异常),但是有时我们写SQL时,检查异常并不会提示;而默认情况下,事物对检查异常不会作出回滚处理。
  • 在很多时候,我们除了catch一般的异常或自定义异常外,我们还习惯于catch住Exception异常;然后再抛出Exception异常。但是Exception异常属于非运行时异常(即:检查异常),因为默认是运行时异常时事物才进行回滚,那么这种情况下,是不会回滚的。我们可以在@Transacional注解中,通过rollbackFor = {Exception.class} 来解决这个问题。即:设置当Exception异常或Exception的所有任意子类异常时事物会进行回滚。
  • 被catch处理了的异常,不会被事物作为判断依据;如果异常被catch 了,但是又在catch中抛出了新的异常,那么事物会以这个新的异常作 为是否进行回滚的判断依据。

事务的传播机制(行为):
事务的传播行为是指,如果在开始当前事务之前,一个事务上下文已经存在,此时有若干选项可以指定一个事务性方法的执行行为。
即:在执行一个@Transactinal注解标注的方法时,开启了事务;当该方法还在执行中时,另一个人也触发了该方法;
那么此时怎么算事务呢,这时就可以通过事务的传播机制来指定处理方式。

在TransactionDefinition定义中包括了如下几个表示传播行为的常量:
常量 含义
TransactionDefinition.PROPAGATION_REQUIRED 如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。这是默认值。
TransactionDefinition.PROPAGATION_REQUIRES_NEW 创建一个新的事务,如果当前存在事务,则把当前事务挂起。
TransactionDefinition.PROPAGATION_SUPPORTS 如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。
TransactionDefinition.PROPAGATION_NOT_SUPPORTED 以非事务方式运行,如果当前存在事务,则把当前事务挂起。
TransactionDefinition.PROPAGATION_NEVER 以非事务方式运行,如果当前存在事务,则抛出异常。
TransactionDefinition.PROPAGATION_MANDATORY 如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。
TransactionDefinition.PROPAGATION_NESTED 如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则该取值等价于TransactionDefinition.PROPAGATION_REQUIRED。
事务补充:

同一个事务里面,对某一条数据的增删改、 都会影响到这个事务里面接下来的对这个条数的增删改查,如(举例部分情况):

说明 结论
一个事务里面,debug未完成时, 数据会入库吗? 不会
一个事务里面,执行一半时,程序莫名停了,数据会回滚吗?
同一个事务里面,插入(数据a) -> 查询(数据a) -> 修改(数据a) -> 插入(数据a),可以吗? 可以
同一个事务里面,插入(数据a) -> 修改(数据a) -> 再次修改(数据a) -> 查询(数据a),可以吗 可以
同一个事务里面,插入(数据a) -> 修改(数据a) -> 删除(数据a),可以吗? 可以
……

参考链接:
http://www.voidcn.com/article/p-bozwatyd-st.html
https://www.ibm.com/developerworks/cn/java/j-master-spring-transactional-use/