Spring Boot 的事务控制

目录

Spring Boot 事务的基本概念

Spring Boot 事务控制主要涉及以下几个概念:

常见传播行为包括:

另外几种事务传播属性:

Spring Boot 中支持如下的隔离级别:

@Transactional注解一般用在哪一层?

@Transactional的rollbackFor属性

Spring Boot 事务控制的基本用法

Spring Boot 事务控制的应用场景

1. 数据库读写操作

2. 执行多个操作

3. 异常处理

4. 并发操作

总结


Spring Boot 提供了简单易用的事务控制功能,方便开发者进行数据库操作时保证数据的一致性和完整性。本文将介绍 Spring Boot 事务控制的用法和应用场景,并提供丰富的例子。

Spring Boot 事务的基本概念

事务是对数据库进行操作时所使用的一种机制,它可以保证在一次操作中所有的 SQL 语句都成功或者都失败。事务的本质是通过将一系列相关的 SQL 语句放在同一个执行环境中,确保在这个执行环境中,所有 SQL 语句的执行状态要么全部成功,要么全部失败,从而保证数据的一致性。

在 Spring Boot 中,我们可以使用 transactional 注解来开启事务。该注解被应用在一个方法上时,Spring 就会将这个方法封装在一个事务中。

@Transactional 可以用在类或者方法上。当我们将其用于类级别的时候,将会应用到所有的方法上。在使用时,我们需要考虑几个方面。

首先,我们需要设置事务的传播行为,即通过 propagation 属性来设置。传播行为指的是在当前事务的环境下,如果新的事务进来,应该如何处理。

Spring Boot 事务控制主要涉及以下几个概念:
  1. 事务管理器(TransactionManager):用于管理事务的启动、提交、回滚等操作,Spring Boot 默认使用 DataSourceTransactionManager 进行事务管理。
  2. 事务注解(@Transactional):用于标记需要进行事务控制的方法,加在类或方法上。
  3. 事务传播(Propagation):表示在一个事务范围内,不同的方法之间如何共享同一个事务。Spring Boot 默认为 REQUIRED。
常见传播行为包括:
  • REQUIRED:如果当前环境已经存在一个事务,就沿用这个事务,否则新开一个事务。
  • SUPPORTS:支持当前事务,如果当前没有事务,就不开启事务。
  • MANDATORY:强制使用当前环境中的事务,如果没有事务就抛出异常。

首先默认值是REQUIRED。这个是什么意思呢?就是标注此注解的方法,默认开启事务。 比如方法A调用了标注@Transactional(propagation = Propagation.REQUIRED)的方法B,那么有两种情况:

  • 情况1:A本身有一个事务,那么此时B就会加入A的事务。
  • 情况2:A本身没有事务,那么此时B就会单独给自己开一个新事务。

所以,标注此注解的意思就是,我自己默认开启事务,其它方法有事务,那我就加入,如果没有,那我就自己新建一个事务。这也是最常用的方式。

另外几种事务传播属性:
  • SUPPORTS,支持事务,有事务就加入,没有就不执行事务。
  • Mandatory 强制事务,如果已经存在一个事务,加入当前事务。如果没有一个活动的事务,则抛出异常。
  • required_new 新事务,总是开启一个新的事务。如果一个事务已经存在,则将这个存在的事务挂起,新启动一个新的。
  • Not_support 不支持事务,总是非事务地执行,并挂起任何存在的事务。
  • Never 禁止事务, 总是非事务地执行,如果存在一个活动事务,则抛出异常
  • Nested 嵌套的 如果有就嵌套、没有就开启事务。这个和默认REQUIRED的区别,主要在失败回滚上,嵌套有保存点的概念,只会局部回滚,而默认属性REQUIRED是全部回滚。

除此之外,我们还需要设置事务的隔离级别。隔离级别指的是在一个事务的环境下,不同的事务应该如何相互隔离。

Spring Boot 中支持如下的隔离级别:
  • DEFAULT:使用数据库默认隔离级别。
  • READ_UNCOMMITTED:事务可以看到其他未提交的事务修改的数据。
  • READ_COMMITTED:事务只能看到其他事务已经提交的数据。
  • REPEATABLE_READ:在同一事务内的查询中返回相同的结果,不受其他事务的影响。
  • SERIALIZABLE:确保事务在并发环境下保持一致性
@Transactional注解一般用在哪一层?

我们既可以添加在Controller层,也可以添加在Service层,甚至可以在Dao层等。但是通常我们是在Service层使用注解的。

  • 在Controller添加事务,通常对系统性能要求比较高,比如我们在Controller层调用Service失败了,立马就要回滚,但是实际上Service层会有多步操作,许多操作不那么重要,不需要回滚。而加在Controoler层,相当于在入口就回滚,对系统安全性等要求很高才需要。
  • 在Dao层添加事务,针对的就是单个操作回滚,对安全性能要求比较低。
  • 在Service添加事务,针对的是多个操作组合回滚,也就是我们上文提到的为什么要使用事务时举的转账例子。我们通常是根据业务情况,来组合几个数据库操作。

所以在Service开启事务,我们可以更合理的规划事务,根据实际情况来针对数据库组合操作来开启事务。

@Transactional的rollbackFor属性

@Transactional注解什么时候生效回滚呢?这个是由rollbackFor属性控制的。默认是rollbackFor=RuntimeException.class,即发生运行时异常回滚。通常我们不管是运行时异常还是受检异常,都需要进行回滚,所以一般我们会设置rollbackFor = Exception.class。即发生任何类型异常,都进行回滚。

注意,如果异常被我们用try-catch捕获,而没有继续抛出,事务是不进行回滚的。

如果需要回滚生效,就需要在catch中继续抛出异常,或者通过代码手动进行回滚。

@Transactional(rollbackFor = Exception.class) 
public void test(){
    try{
    ...
    }catch{
    ...
       //继续抛出异常回滚
       throw new Exception();
    }
}
@Transactional(rollbackFor = Exception.class)
public void test(){
    try{
        ...
    }catch{
    ...
        //手动回滚
        TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
    }
}

最后,我们还需要设置事务的超时时间和回滚规则。超时时间指的是一个事务的最长执行时间;回滚规则指的是当一个事务发生错误时应该如何处理,例如应该将错误信息写入日志,或者回滚整个事务等等。

在 Spring Boot 中,我们可以使用如下的方式配置事务:

@Transactional(propagation = Propagation.REQUIRED,
isolation = Isolation.DEFAULT,
timeout = 3600,
readOnly = false,
rollbackFor = Exception.class)

Spring Boot 事务是应用在企业级应用程序中非常重要的机制之一。只有掌握了 Spring Boot 事务的基本概念,我们才能更好地运用 Spring Boot 来开发高质量的应用程序。

Spring Boot 事务控制的基本用法

下面通过一个简单的例子来介绍事务控制的基本用法。

假设有一个基于 Spring Data JPA 的用户服务,提供增加用户和查询用户信息两个方法:

UserService:

@Service
public class UserService {
@Autowired
private UserRepository userRepository;

@Transactional
public void createUser(User user) {
userRepository.save(user);
}

@Transactional(readOnly = true)
public User getUserById(Long id) {
return userRepository.findById(id).orElse(null);
}
}

UserRepository:

public interface UserRepository extends JpaRepository, Long> {
}

其中,@Transactional 注解标记两个方法,表示这两个方法需要进行事务控制。createUser 方法用于向数据库增加用户信息,getUserById 方法用于通过用户 ID 查询用户信息。

Spring Boot 事务控制的应用场景

1. 数据库读写操作

对于数据库读写操作,开发者通常会想到使用事务控制,如下所示:

@Transactional
public void doReadWrite() {
    userDao.save(user);
    userDao.findById(id);
}

在这个例子中,我们使用 @Transactional 注解标识方法,执行两个操作:先插入 user 对象,再查询该对象。由于使用了事务管理,如果插入操作抛出异常,该事务将回滚,插入操作对数据库不起作用。

2. 执行多个操作

在执行多个操作时,如果其中一个操作失败,我们希望所有操作都不会完成。此时就需要使用事务来保证数据的完整性。

例如下面的代码:

@Transactional
public void doMultipleOps() {
    userDao.save(user);
    groupDao.save(group);
}

如果 userDao.save 保存成功,而 groupDao.save 抛出异常,该事务将回滚,user 对象并不会被存储在数据库中。

3. 异常处理

在数据库操作中,经常会出现各种异常,如数据重复、SQL 语句错误、数据库连接中断等。在这种情况下,我们希望能够对异常进行捕获和处理,以保证程序稳定性和安全性。

例如下面的代码:

@Transactional
public void doExceptionHandle() {
    try {
        userDao.save(user);
        groupDao.save(group);
    } catch (Exception e) {
        log.error("操作失败:" + e.getMessage());
    }
}

在这个例子中,我们使用 try-catch 针对异常进行处理,在数据库操作出现异常的情况下,给出错误提示并回滚事务。

4. 并发操作

在并发操作中,需要考虑多个线程或进程之间的数据同步问题。在这种情况下,事务控制能够很好地解决这个问题。

例如下面的代码:

@Autowired
private UserService userService;

class AddUserThread implements Runnable {
    @Override
    public void run() {
        User user = new User();
        user.setUsername("test");
        userService.createUser(user);
    }
}

ExecutorService threadPool = Executors.newFixedThreadPool(10);
Runnable target = new AddUserThread();
for (int i = 0; i < 100; i++) {
    threadPool.execute(target);
}

在这个例子中,我们使用了线程池同时向数据库中插入 100 个用户。由于每个用户的插入需进行事务控制,因此可以确保并发操作的数据同步性。

总结

Spring Boot 提供了简单易用的事务控制功能,方便开发者进行数据库操作时保证数据的一致性和完整性。

你可能感兴趣的:(spring,boot,数据库,后端)