【Spring Boot 事务管理】

Spring Boot 事务管理

一、Spring Boot中的事务管理

1.声明式事务管理

@Transactional注解

Spring最常用的事务管理方式是声明式事务管理,主要通过@Transactional注解实现。这种方式的优点在于简单易用,通过将注解添加到类或方法上,开发者可以轻松地控制事务的边界,而无需直接与底层的事务管理API打交道。

基本使用

在方法级别上使用@Transactional注解,可以确保该方法内的所有操作要么全部成功,要么在遇到异常时全部回滚。

java 复制代码
import org.springframework.transaction.annotation.Transactional;

@Transactional
public void updateUserData(User user) {
    // 这里的数据库操作会在一个事务中执行
}
配置选项

@Transactional注解提供了多种配置选项,包括事务的传播行为、隔离级别、超时设置等。

2.编程式事务管理

使用TransactionTemplate或直接使用PlatformTransactionManager,可以在代码中精确控制事务的边界。

TransactionTemplate

java 复制代码
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.support.TransactionTemplate;

public class UserService {

    @Autowired
    private TransactionTemplate transactionTemplate;

    public User createUser(final User user) {
        return transactionTemplate.execute(status -> {
            // 这里的操作会在一个事务中执行
            return userRepository.save(user);
        });
    }
}

PlatformTransactionManager

对于需要完全控制事务行为的场景,Spring还允许直接使用PlatformTransactionManager。

java 复制代码
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;

public class UserServiceImpl implements UserService {

    @Autowired
    private PlatformTransactionManager transactionManager;

    public void updateUser(User user) {
        TransactionStatus status = transactionManager.getTransaction(new DefaultTransactionDefinition());
        try {
            // 这里的操作会在一个事务中执行
            userRepository.update(user);
            transactionManager.commit(status);
        } catch (Exception e) {
            transactionManager.rollback(status);
            throw e;
        }
    }
}

二、@Transactional注解深入

1.基本使用

@Transactional注解可以应用于类或方法级别。当应用于类级别时,该类中的所有公共方法都会被应用事务管理。当应用于方法级别时,只有标注了该注解的方法才会进行事务管理。

基本属性

  • readOnly: 指定事务是否为只读事务。只读事务可以帮助数据库引擎优化事务。
  • propagation: 指定事务的传播行为。
  • isolation: 指定事务的隔离级别。
  • timeout: 定义事务的超时限制(以秒为单位)。
  • rollbackFor: 指定哪些异常可以触发事务回滚。
  • noRollbackFor: 指定哪些异常不应触发事务回滚。

2.传播行为

事务的传播行为定义了事务边界的创建方式。Spring定义了多种传播行为:

  • REQUIRED: 如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。
  • SUPPORTS: 如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务方式执行。
  • MANDATORY: 如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。
  • REQUIRES_NEW: 总是创建一个新的事务,如果当前存在事务,则挂起当前事务。
  • NOT_SUPPORTED: 总是以非事务方式执行,如果当前存在事务,则挂起当前事务。
  • NEVER: 总是以非事务方式执行,如果当前存在事务,则抛出异常。
  • NESTED : 如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则表现同REQUIRED

3.隔离级别

隔离级别定义了一个事务可能受其他并发事务影响的程度。Spring支持以下隔离级别:

  • DEFAULT: 使用底层数据源的默认隔离级别。
  • READ_UNCOMMITTED: 允许读取未提交的更改。
  • READ_COMMITTED: 仅允许读取已提交的更改。
  • REPEATABLE_READ: 确保在事务内重复读取同一记录的结果一致。
  • SERIALIZABLE: 完全隔离,确保事务串行化执行。

4.事务超时设置

通过@Transactional注解的timeout属性,可以为事务指定超时时间。如果事务执行时间超过了指定的时间限制,Spring将自动回滚事务。

5.回滚规则

通过@Transactional注解的rollbackFornoRollbackFor属性,可以精确控制异常回滚行为:

  • rollbackFor: 指定哪些异常应该触发事务回滚。
  • noRollbackFor: 指定哪些异常不应该触发事务回滚。

三、事务管理的最佳实践

1.事务边界的确定

事务边界定义了事务的开始和结束,合理的事务边界可以确保事务既不过大也不过小。

  • 定义清晰的业务逻辑单元:每个事务应该对应一个清晰定义的业务逻辑单元。不应该让一个事务覆盖多个不相关的操作。
  • 避免长事务:长事务会占用数据库资源,增加锁定的范围和时间,从而影响并发性能。尽量避免不必要的长事务,及时提交或回滚。
  • 事务中的操作数量:虽然理论上事务可以包含任意数量的操作,但是在实践中,应该避免在单一事务中包含过多的操作。如果业务逻辑允许,可以将一个大事务分解为几个小事务,以提高并发性和系统稳定性。

2.只读事务的使用

只读事务是指不包含任何修改(插入、更新、删除)操作的事务。只读事务的使用有以下好处:

  • 性能优化:数据库可以对只读事务进行优化,如减少锁的使用,提升查询性能。
  • 减少副作用:标记为只读的事务明确告诉数据库和应用程序这个事务不会修改数据,有助于避免由于错误的数据修改导致的问题。

何时使用只读事务:

  • 数据查询操作:当事务仅包含数据查询操作,不涉及任何数据修改时,应将事务标记为只读。
  • 报表生成:生成报表或执行大量查询以分析数据时,使用只读事务可以提高效率。

3.避免编程式事务

尽管Spring提供了编程式事务管理的能力,但在大多数情况下,推荐使用声明式事务管理(@Transactional注解)原因如下:

  • 简化开发:声明式事务管理通过注解的方式,减少了编程的复杂度,使得事务管理更加直观和易于理解。
  • 减少代码侵入性:使用声明式事务管理,事务代码与业务代码分离,降低了代码的耦合度,提高了代码的可读性和可维护性。
  • 统一事务管理:声明式事务管理提供了一种统一的事务管理机制,使得事务管理更加标准化,易于跟踪和维护。
相关推荐
marsjin26 分钟前
MYSQL多个表进行笛卡尔积查询优化
数据库·mysql
数据发现1 小时前
昆虫学(书籍学习资料)
数据库·数据挖掘·数据分析
虫小宝3 小时前
如何在Java中实现批量数据处理
java·开发语言
king888866663 小时前
Java中的AQS
java
冰暮流星4 小时前
软设之类的继承与泛化,多重继承
java·开发语言
虫小宝4 小时前
Java中的多线程与并发编程详解
java·开发语言
oNuoyi4 小时前
定位线上同步锁仍然重复扣费的Bug定位及Redis分布式锁解决方案
java·spring boot·redis·分布式
Easonmax4 小时前
【C++】 解决 C++ 语言报错:Undefined Reference
java·开发语言·c++
计算机平台作业答案讲解4 小时前
QT实现GIF动图显示(小白版,可直接copy使用)
服务器·开发语言·数据结构·数据库·c++·qt·动态规划
王大锤43914 小时前
idea导入opencv和mediapipe
java·opencv·intellij-idea