Skip to content

SpringBoot 事务注解

SpringBoot 提供了非常方便的事务操作,通过注解就可以实现事务的回滚,非常方便快捷,下面我们就说一下如何进行事务操作。

1. 事务说明

在 Spring 中,事务有两种实现方式,分别是 编程式事务管理声明式事务管理 两种方式。

编程式事务管理:编程式事务管理使用 TransactionTemplate 或者直接使用底层的 PlatformTransactionManager;对于编程式事务管理,spring 推荐使用 TransactionTemplate;

声明式事务管理:建立在 AOP 之上的;其本质是对方法前后进行拦截,然后在目标方法开始之前创建或者加入一个事务,在执行完目标方法之后根据执行情况提交或者回滚事务。

声明式事务管理不需要入侵代码,通过 @Transactional 就可以进行事务操作,更快捷而且简单。

2. 如何使用

  • 开启事务管理

在启动类上增加以下注解以启用事务管理功能:

java
//启注解事务管理,等同于 xml 配置方式的 <tx:annotation-driven />
@EnableTransactionManagement
  • 使用事务

在需要开启事务的方法上增加注解 @Transactional 即可:

java
@Transactional
public Object transtionalManage() {

    // db操作1
    // db操作2
    // db操作3

    return "Success";
}

注解 @Transactional 可以作用于接口、接口方法、类以及类方法上;当作用于类上时,该类的所有 public 方法将都具有该类型的事务属性,同时,我们也可以在方法级别使用该标注来覆盖类级别的定义。

3. 常用配置

注解 @Transactional 提供了众多属性供不同的业务场景使用,常用的配置信息如下:

4. 事务属性

4.1 事务隔离级别

隔离级别是指若干个并发的事务之间的隔离程度。TransactionDefinition 接口中定义了五个表示隔离级别的常量:

隔离级别说明
TransactionDefinition.ISOLATION_DEFAULT这是默认值,表示使用底层数据库的默认隔离级别。对于大部分数据库而言,通常这个值是 ISOLATION_READ_COMMITTED
TransactionDefinition.ISOLATION_READ_UNCOMMITTED该隔离级别表示一个事务可以读取另一个事务修改但还没有提交的数据。该级别不能防止脏读,不可重复读和幻读,因此很少使用该隔离级别。比如 PostgreSQL 实际上并没有此级别
TransactionDefinition.ISOLATION_READ_COMMITTED该隔离级别表示一个事务只能读取另一个事务已经提交的数据。该级别可以防止脏读,这也是大多数情况下的推荐值
TransactionDefinition.ISOLATION_REPEATABLE_READ该隔离级别表示一个事务在整个过程中可以多次重复执行某个查询,并且每次返回的记录都相同。该级别可以防止脏读和不可重复读
TransactionDefinition.ISOLATION_SERIALIZABLE所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读。但是这将严重影响程序的性能。通常情况下也不会用到该级别

4.2 事务传播行为

所谓事务的传播行为是指,如果在开始当前事务之前,一个事务上下文已经存在,此时有若干选项可以指定一个事务性方法的执行行为。在 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

4.3 事务超时

所谓事务超时,就是指一个事务所允许执行的最长时间,如果超过该时间限制但事务还没有完成,则自动回滚事务。在 TransactionDefinition 中以 int 的值来表示超时时间,其单位是秒。

默认设置为底层事务系统的超时值,如果底层数据库事务系统没有设置超时值,那么就是 none,没有超时限制。

java
@Transactional(propagation=Propagation.REQUIRED, timeout=30)

4.4 事务只读属性

只读事务用于客户代码只读但不修改数据的情形,只读事务用于特定情景下的优化,比如使用 Hibernate 的时候。默认为读写事务。

5. 事务管理器

关于事务管理器,不管是 JPA 还是 JDBC 等都实现自接口 PlatformTransactionManager,如果项目中添加的是 spring-boot-starter-jdbc 依赖,框架会默认注入 DataSourceTransactionManager 实例。如果添加的是 spring-boot-starter-data-jpa 依赖,框架会默认注入 JpaTransactionManager 实例。

可以在启动类中添加如下方法,Debug 测试,就能知道自动注入的是 PlatformTransactionManager 接口的哪个实现类:

java
@SpringBootApplication
@EnableTransactionManagement
@Slf4j
public class ProfiledemoApplication {

    @Bean
    public Object testBean(PlatformTransactionManager platformTransactionManager){
        log.debug("自动注入的事务管理器 - {}", platformTransactionManager.getClass().getName());
        return new Object();
    }

    public static void main(String[] args) {
        SpringApplication.run(ProfiledemoApplication.class, args);
    }
}

这些 SpringBoot 为我们自动做了,这些对我们并不透明,如果项目做的比较大,添加的持久化依赖比较多,我们还是会选择人为的指定使用哪个事务管理器。

5.1 指定事务管理器

java
@SpringBootApplication
@EnableTransactionManagement
@Slf4j
public class ProfiledemoApplication {

    // 其中 dataSource 框架会自动为我们注入
    @Bean
    public PlatformTransactionManager txManager(DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }

    @Bean
    public Object testBean(PlatformTransactionManager platformTransactionManager) {
        log.debug("自动注入的事务管理器 - {}", platformTransactionManager.getClass().getName());
        return new Object();
    }

    public static void main(String[] args) {
        SpringApplication.run(ProfiledemoApplication.class, args);
    }
}

在 Spring 容器中,我们手工注解 @Bean 将被优先加载,框架不会重新实例化其他的 PlatformTransactionManager 实现类。

5.2 使用指定的事务管理器

java
@SpringBootApplication
@EnableTransactionManagement
public class ProfiledemoApplication implements TransactionManagementConfigurer {

    @Resource(name="txManager2")
    private PlatformTransactionManager txManager2;

    // 创建事务管理器1
    @Bean(name = "txManager1")
    public PlatformTransactionManager txManager(DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }

    // 创建事务管理器2
    @Bean(name = "txManager2")
    public PlatformTransactionManager txManager2(EntityManagerFactory factory) {
        return new JpaTransactionManager(factory);
    }

    // 实现接口 TransactionManagementConfigurer 方法,其返回值代表在拥有多个事务管理器的情况下默认使用的事务管理器
    @Override
    public PlatformTransactionManager annotationDrivenTransactionManager() {
        return txManager2;
    }

    public static void main(String[] args) {
        SpringApplication.run(ProfiledemoApplication.class, args);
    }
}

@Component
public class DevSendMessage implements SendMessage {

    // 使用value具体指定使用哪个事务管理器
    @Transactional(value="txManager1")
    @Override
    public void send() {
        System.out.println(">>>>>>>>Dev Send()<<<<<<<<");
        send2();
    }

    // 在存在多个事务管理器的情况下,如果使用value具体指定
    // 则默认使用方法 annotationDrivenTransactionManager() 返回的事务管理器
    @Transactional
    public void send2() {
        System.out.println(">>>>>>>>Dev Send2()<<<<<<<<");
    }
}

6. 注意事项

事务注解一定不要加在类上,即使 Spring 支持这么做;

事务注解加在方法上时,该方法必须为 public,不然事务失效;

当一个接口中除了有多个查询 SQL 语句,若再无其他 SQL 语句,此时没必要加事务注解。