网站Logo Ilren 小记

Spring 事务管理全解:从原理到实战

jack
2
2023-05-11

引言

在日常的 Java 企业开发中,事务管理是保障数据一致性和系统可靠性的关键一环。Spring 框架通过抽象事务机制,大大简化了开发者在多数据源、分布式环境中的事务控制工作。本文将从基本概念入手,全面解析 Spring 的事务管理机制,涵盖使用方式、常见注解配置、事务传播行为及常见陷阱等,帮助你更好地在项目中运用事务控制。

一、事务基础概念

在数据库系统中,事务(Transaction)是一组操作的集合,这些操作要么全部执行成功,要么全部回滚,不能只执行一部分。一个完整的事务需要满足 ACID 四大特性

特性

英文

说明

原子性

Atomicity

事务内的所有操作要么全部成功,要么全部失败

一致性

Consistency

执行前后数据库状态必须一致

隔离性

Isolation

多个事务并发执行时应彼此隔离

持久性

Durability

一旦事务提交,其结果应永久保留

在真实业务场景中,例如“转账”操作,若扣款成功但未成功入账,就会出现资金丢失的问题,这就需要使用事务来保证数据操作的一致性。

二、Spring 事务管理方式

Spring 的事务管理提供了两种主流方式:

方式

说明

场景适用

编程式事务

使用 TransactionTemplatePlatformTransactionManager 明确指定事务边界

灵活性强,适合特殊场景

声明式事务

使用注解或 XML 配置,由 Spring AOP 自动代理处理事务

开发效率高,推荐使用

开发中我们常用 声明式事务 来实现自动事务控制,因为它简洁高效、可维护性强。

三、声明式事务详解(主流方式)

1. 使用注解快速实现事务控制

只需在需要控制事务的方法或类上加上 @Transactional 注解即可:

@Service
public class AccountService {

    @Transactional
    public void transfer(String from, String to, double amount) {
        accountDao.debit(from, amount); // 扣款
        accountDao.credit(to, amount);  // 入账
    }
}
  • 默认情况下,注解作用于 public 方法。

  • 类上加 @Transactional,表示所有 public 方法都开启事务。

2. Spring Boot 中事务管理器的配置(可选)

@Configuration
@EnableTransactionManagement
public class TransactionConfig {

    @Bean
    public PlatformTransactionManager transactionManager(DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }
}

在 Spring Boot 中,如果你使用的是 JDBC 或 JPA 等主流技术,Spring 会自动配置事务管理器,你只需添加注解即可。

四、事务注解常用配置参数

属性

类型

说明

propagation

枚举

事务传播行为(默认 REQUIRED

isolation

枚举

事务隔离级别(如 READ_COMMITTED

rollbackFor

Class[]

指定哪些异常触发回滚

noRollbackFor

Class[]

指定哪些异常不触发回滚

readOnly

boolean

是否只读事务(默认 false)

timeout

int

超时时间(秒)

示例:

@Transactional(
    propagation = Propagation.REQUIRED,
    isolation = Isolation.READ_COMMITTED,
    rollbackFor = Exception.class,
    timeout = 10
)

五、事务传播行为(Propagation)

在复杂业务调用中,方法之间可能会嵌套调用事务方法。Spring 提供了多种传播行为以适应不同场景:

类型

说明

REQUIRED

如果存在事务就加入,否则新建一个(默认)

REQUIRES_NEW

总是新建事务,挂起当前事务

NESTED

嵌套事务,使用保存点,依赖底层数据库支持

SUPPORTS

有事务就加入,没有也正常执行

NOT_SUPPORTED

不支持事务,若有事务则挂起

NEVER

不允许事务,存在事务就抛异常

MANDATORY

必须存在事务,否则抛异常

示例说明

@Transactional(propagation = Propagation.REQUIRES_NEW)
public void saveLog() {
    // 保存日志,新建独立事务
}

六、事务隔离级别(Isolation)

数据库并发操作时,为避免“脏读”、“不可重复读”、“幻读”等问题,可设置事务的隔离级别:

隔离级别

说明

能防止的问题

READ_UNCOMMITTED

允许读取未提交数据

READ_COMMITTED

只能读取已提交数据

脏读

REPEATABLE_READ

同一事务中读取数据一致

脏读、不可重复读

SERIALIZABLE

串行执行事务,最高级别

所有并发问题

MySQL 默认使用 REPEATABLE_READ,Oracle 使用 READ_COMMITTED

七、事务回滚机制

默认情况下:

  • Spring 只对 RuntimeException(运行时异常) 和 Error 回滚。

  • 对于 Checked Exception(受检异常),不会回滚,除非指定 rollbackFor

例子:

@Transactional(rollbackFor = Exception.class)
public void doSomething() throws IOException {
    // 抛出受检异常也会触发回滚
}

八、编程式事务控制(灵活但冗长)

适合特殊情况下的事务细粒度控制:

@Transactional
public class MyService {
    @Autowired
    private PlatformTransactionManager txManager;

    public void doWork() {
        TransactionStatus status = txManager.getTransaction(new DefaultTransactionDefinition());
        try {
            // do something
            txManager.commit(status);
        } catch (Exception e) {
            txManager.rollback(status);
        }
    }
}

九、常见事务失效场景与排查

现象

原因分析

事务不生效

方法非 public,或不是通过 Spring 代理调用

内部方法调用不回滚

自调用不会经过 Spring AOP

异常被 catch 后事务未回滚

异常未抛出,Spring 不知道异常

使用了 @Transactional(readOnly = true) 导致写操作失败

只读事务会限制写操作

解决办法:

  • 保证事务方法是 public

  • 不要在同一个类中直接调用带事务的方法

  • 异常捕获后重新抛出或手动回滚

  • 正确使用 rollbackFor 指定异常类型

十、总结与最佳实践

  • 优先使用声明式事务,搭配 @Transactional 注解

  • 只在真正需要回滚时开启事务,避免不必要的性能损耗

  • 尽量避免嵌套调用事务方法,或使用 AOP 解耦

  • 配置 rollbackFor 以确保受检异常也能回滚

  • 记录事务边界的设计文档,有助于维护和排查问题

结语

Spring 的事务管理机制为我们开发复杂业务逻辑提供了坚实保障。理解其底层原理与配置选项,掌握事务传播行为与隔离级别,不仅能提升代码质量,更能降低系统出错概率。希望本文能帮助你在项目中更高效地使用事务管理。

动物装饰