javaweb-Day10-Spring 事务管理笔记

TJCcc 发布于 5 天前 2 次阅读


1. 事务概述

事务(Transaction)是一组操作的执行单元,具备 ACID 四大特性:

  • 原子性(Atomicity):全部成功或全部失败
  • 一致性(Consistency):数据状态保持合法
  • 隔离性(Isolation):并发事务互不干扰
  • 持久性(Durability):提交后永久生效

在企业级应用中,我们通常使用 Spring 事务管理 来简化事务控制,它屏蔽了底层持久化技术(JDBC、Hibernate、JPA 等)的差异,提供统一的事务抽象。


2. Spring 事务管理基础

Spring 提供两种事务管理方式:

2.1 编程式事务管理

通过 TransactionTemplatePlatformTransactionManager 手动控制事务的提交与回滚。

@Service
public class UserService {

    @Autowired
    private TransactionTemplate transactionTemplate;

    public void updateUser() {
        transactionTemplate.execute(status -> {
            try {
                // 业务操作
                userDao.update(user);
                orderDao.save(order);
                return Boolean.TRUE;
            } catch (Exception e) {
                status.setRollbackOnly(); // 标记回滚
                return Boolean.FALSE;
            }
        });
    }
}

优点:精细控制
缺点:侵入性强,代码冗余,不推荐在业务层大量使用。

2.2 声明式事务管理(推荐)

基于 AOP 实现,通过 @Transactional 注解或 XML 配置,无侵入地将事务织入方法。

@Service
@Transactional(rollbackFor = Exception.class)
public class UserService {

    @Autowired
    private UserDao userDao;

    public void register(User user) {
        userDao.insert(user);
        // 其他业务操作
    }
}

使用要点

  • 在 Spring Boot 中,引入 spring-boot-starter-jdbcspring-boot-starter-data-jpa 后,事务管理自动配置。
  • 需要在启动类或配置类上添加 @EnableTransactionManagement(Spring Boot 默认开启)。
  • 通常将 @Transactional 标注在 Service 层方法 上。

3. 事务属性详解(进阶)

@Transactional 注解提供了丰富的属性,用于定制事务行为。

3.1 传播行为(Propagation)

定义了事务方法被另一个事务方法调用时,如何传播事务边界。

传播行为含义
REQUIRED(默认)当前存在事务则加入,否则新建事务
REQUIRES_NEW始终新建事务,挂起当前事务(如果存在)
NESTED当前存在事务则嵌套执行,否则新建(依赖 JDBC savepoint)
SUPPORTS当前存在事务则加入,否则以非事务方式执行
NOT_SUPPORTED以非事务方式执行,挂起当前事务
MANDATORY必须存在事务,否则抛异常
NEVER必须不存在事务,否则抛异常

使用示例

@Service
public class OrderService {

    @Transactional(propagation = Propagation.REQUIRED)
    public void createOrder(Order order) {
        // 加入当前事务
    }

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void logAudit(String message) {
        // 始终新开事务,不受外部事务回滚影响
    }
}

注意

  • REQUIRES_NEW 会挂起外部事务,新事务独立提交/回滚。
  • NESTED 使用 savepoint,外部事务回滚会回滚嵌套事务,但嵌套事务回滚不影响外部事务(JDBC 3.0 以上支持)。

3.2 隔离级别(Isolation)

解决并发事务带来的脏读、不可重复读、幻读问题。

隔离级别脏读不可重复读幻读
READ_UNCOMMITTED
READ_COMMITTED
REPEATABLE_READ是(MySQL InnoDB 默认)
SERIALIZABLE

Spring 中默认使用底层数据库的默认隔离级别(如 MySQL 为 REPEATABLE_READ,Oracle 为 READ_COMMITTED)。

@Transactional(isolation = Isolation.READ_COMMITTED)
public void updateStock() { ... }

设置隔离级别需确保数据库支持,且会降低并发性能,按需使用。

3.3 超时(Timeout)

指定事务的最长执行时间(秒),超时则自动回滚。

@Transactional(timeout = 30)  // 单位:秒
public void batchProcess() { ... }
  • 默认值为 -1(不超时)。
  • 超时计时从事务开始到方法结束,若超时抛出 TransactionTimedOutException

3.4 只读(ReadOnly)

标记事务为只读,有助于数据库优化(如 Hibernate 的 flush 模式),但不强制数据不修改。

@Transactional(readOnly = true)
public List<User> findUsers() { ... }
  • 通常用于查询方法。
  • 如果只读事务中执行了写操作,某些数据库或 ORM 框架会抛出异常。

3.5 回滚规则(rollbackFor / noRollbackFor)

这是开发中最容易踩坑的属性,务必掌握!

Spring 事务默认的回滚策略:

  • 运行时异常(RuntimeException)错误(Error) 发生时,自动回滚。
  • 受检异常(Checked Exception,即 Exception 的子类非 RuntimeException) 发生时,不回滚

由于业务中通常希望任何异常都回滚,我们需要显式配置:

// 对所有 Exception 及其子类回滚(包括受检异常)
@Transactional(rollbackFor = Exception.class)
public void transferMoney() throws Exception { ... }

// 指定多个异常
@Transactional(rollbackFor = {SQLException.class, IOException.class})
public void uploadFile() { ... }

// 不回滚指定的异常(即使它是 RuntimeException)
@Transactional(noRollbackFor = NullPointerException.class)
public void process() { ... }

最佳实践
建议在 Service 类的 类级别 配置 @Transactional(rollbackFor = Exception.class),统一处理,避免遗漏。


4. 事务管理原理(源码级别)

Spring 声明式事务底层基于 AOP 动态代理(JDK Proxy 或 CGLIB)。核心组件:

  • TransactionInterceptor:方法拦截器,负责获取事务属性、开启事务、执行目标方法、提交/回滚。
  • PlatformTransactionManager:事务管理器接口,具体实现如 DataSourceTransactionManager(JDBC)、JpaTransactionManagerHibernateTransactionManager
  • TransactionStatus:当前事务状态,包含是否是新事务、是否有 savepoint、是否只回滚等。

简化流程:

  1. 代理对象调用目标方法。
  2. TransactionInterceptor 根据 @Transactional 属性创建或加入事务。
  3. 执行目标方法(业务逻辑)。
  4. 若抛出异常且符合回滚规则,则回滚;否则提交。

5. 常见问题与注意事项

5.1 自调用失效

在同一个类中,一个非事务方法调用另一个标注了 @Transactional 的方法,事务不会生效,因为 Spring 的代理无法拦截内部调用。

解决方案

  • 将事务方法移到另一个 Service 中。
  • 通过 AopContext.currentProxy() 获取代理对象调用自身(需暴露代理)。
@Service
public class UserService {

    @Autowired
    private UserService self; // 注入自身(Spring 2.6+ 需允许循环依赖)

    @Transactional
    public void doTransaction() { ... }

    public void doNonTransaction() {
        // self.doTransaction(); // 通过代理调用
        // 或 ((UserService) AopContext.currentProxy()).doTransaction();
    }
}

5.2 事务管理器配置

多数据源场景需指定 transactionManager

@Transactional(transactionManager = "secondaryTransactionManager")
public void saveToSecondaryDb() { ... }

5.3 事务回滚与异常处理

  • 事务方法内捕获异常后若不重新抛出,事务会正常提交(不会回滚),需谨慎。
  • 推荐在 Service 层向外抛出异常,由上层(如 Controller 或全局异常处理器)统一处理。
@Transactional(rollbackFor = Exception.class)
public void update() {
    try {
        // 业务操作
    } catch (Exception e) {
        // 必须重新抛出,否则事务不会回滚
        throw new RuntimeException("业务异常", e);
    }
}

5.4 只读事务优化

readOnly = true 在底层可能开启数据库只读事务,有助于性能(如 Hibernate 跳过脏检查),但并非所有数据库都支持。

5.5 传播行为与嵌套事务

  • REQUIRES_NEW 会挂起当前事务,消耗数据库连接,慎用。
  • NESTED 使用 savepoint,仅当底层支持(如 JDBC 3.0),且 PlatformTransactionManager 支持嵌套事务(如 DataSourceTransactionManager)。

6. 总结

属性关键点
rollbackFor默认仅回滚 RuntimeException 和 Error;务必设为 Exception.class 以覆盖受检异常
propagation默认 REQUIRED,根据业务场景选择合适的传播行为
isolation默认使用数据库隔离级别,按需提高
timeout防止长事务,设置合理超时
readOnly查询方法可开启,提升性能

开发准则

  • 事务应加在 Service 层,粒度适中,不宜过大(避免长事务锁表)。
  • 类级别统一设置 rollbackFor = Exception.class,避免遗漏。
  • 注意自调用失效问题,确保事务通过代理对象执行。
  • 单元测试中可通过 @Transactional 默认回滚(@Rollback)保证测试数据干净。
唯有极致沉淀,才能造就辉煌。
最后更新于 2026-08-20