TransactionManager

TransactionManager

TransactionManager 是什么

TransactionManager 直译是事务管理器,它是 Java 中负责统一管理事务全生命周期的核心组件,它封装了所有和事务相关的操作,以及事务规则的执行,都由它来统一调度和控制。解决了传统手动管理事务(如 JDBC 中 conn.setAutoCommit(false))的繁琐、易出错问题,尤其在多数据源、分布式场景下提供了统一的事务管理抽象。

根据管理的事务范围(资源协调边界),Java 中的 TransactionManager 主要分为两类:

  • 本地事务管理器(Local TransactionManager)

    用于协调单一独立资源节点内的事务,仅保障该资源节点内的事务 ACID 特性,依赖资源自身的原生事务能力实现。

    典型实现:

    • DataSourceTransactionManager:适配基于 JDBC 的关系型数据源(MySQL/Oracle 等),Spring 生态最常用的本地事务管理器;
    • HibernateTransactionManager:适配 Hibernate ORM 框架,与 Hibernate Session 生命周期绑定;
    • JpaTransactionManager:适配 JPA 规范实现(如 Hibernate JPA、EclipseLink);
    • RedisTransactionManager:适配 Redis 单实例/单机集群的事务操作;
    • JtaTransactionManager(本地模式):仅适配支持 JTA 规范的单一本地数据源(非分布式场景);
  • 分布式事务管理器(Distributed TransactionManager)

    针对跨多个独立资源节点(多数据源、跨微服务节点等)的事务管理,核心目标是保证分布式场景下多个资源的事务一致性。

    典型实现(按一致性保障策略细分):

    • 强一致性分布式事务管理器(基于 JTA/XA 标准):Atomikos、Bitronix、JBossTS(Narayana);
    • 柔性一致性分布式事务管理器(新一代):
      Seata(阿里开源):支持 AT/TCC/SAGA/XA 四种模式,适配微服务场景;
      RocketMQ 事务消息:基于消息最终一致性的分布式事务方案;
      ShardingSphere-JDBC:分库分表场景下的分布式事务管理器(支持 XA/SAGA/TCC);

核心接口与标准

JTA 标准的 TransactionManager 接口

JTA 是 Java EE 定义的事务规范,javax.transaction.TransactionManager 是其核心接口,定义了事务管理的核心方法:

java 复制代码
import javax.transaction.TransactionManager;
import javax.transaction.NotSupportedException;
import javax.transaction.SystemException;
import javax.transaction.RollbackException;

public class JtaTransactionDemo {
    public static void main(String[] args) {
        // 获取JTA事务管理器实例(不同实现方式获取方式不同)
        TransactionManager tm = com.atomikos.icatch.jta.UserTransactionManager();
        
        try {
            // 1. 开启事务
            tm.begin();
            
            // 2. 执行业务逻辑(操作多个数据源)
            doBusinessLogic();
            
            // 3. 提交事务
            tm.commit();
        } catch (NotSupportedException | SystemException e) {
            // 事务开启失败
            e.printStackTrace();
        } catch (RollbackException e) {
            // 事务提交失败(已自动回滚)
            e.printStackTrace();
        } catch (Exception e) {
            // 业务异常,手动回滚
            try {
                tm.rollback();
            } catch (SystemException se) {
                se.printStackTrace();
            }
        }
    }
    
    private static void doBusinessLogic() {
        // 操作多个数据源的业务逻辑
    }
}

核心方法说明:

方法 作用
begin() 开启一个新事务
commit() 提交当前事务(仅当事务状态正常时)
rollback() 回滚当前事务
setRollbackOnly() 标记事务为只能回滚(后续无法提交)
suspend() 挂起当前事务,返回挂起的事务对象
resume(Transaction tx) 恢复被挂起的事务
getStatus() 获取事务当前状态(如活动、已提交、已回滚)

Spring 的 PlatformTransactionManager

Spring 对事务管理器做了抽象,提供 org.springframework.transaction.PlatformTransactionManager 接口,屏蔽了底层不同事务管理器的差异,是 Spring 事务管理的核心,接口源码如下:

java 复制代码
public interface PlatformTransactionManager {
    // 1. 获取/开启事务
    TransactionStatus getTransaction(@Nullable TransactionDefinition definition) throws TransactionException;

    // 2. 提交事务
    void commit(TransactionStatus status) throws TransactionException;

    // 3. 回滚事务
    void rollback(TransactionStatus status) throws TransactionException;
}

getTransaction():获取/开启事务

java 复制代码
TransactionStatus getTransaction(@Nullable TransactionDefinition definition) throws TransactionException;

入参 TransactionDefinition 定义了事务的所有属性(传播行为、隔离级别、超时时间等),接口核心方法如下(同时定义了属性的默认常量,如 PROPAGATION_REQUIRED(默认传播行为)、ISOLATION_DEFAULT(默认隔离级别)):

java 复制代码
public interface TransactionDefinition {
    // 传播行为
    int getPropagationBehavior();

    // 隔离级别
    int getIsolationLevel();

    // 超时时间(秒)
    int getTimeout();

    // 是否只读
    boolean isReadOnly();

    // 事务名称
    @Nullable
    String getName();
}

返回值 TransactionStatus:表示当前事务的实时状态,核心方法如下:

java 复制代码
public interface TransactionStatus extends TransactionExecution, SavepointManager {
    // 是否是新事务
    boolean isNewTransaction();

    // 是否有保存点
    boolean hasSavepoint();

    // 设置为仅回滚
    void setRollbackOnly();

    // 是否标记为仅回滚
    boolean isRollbackOnly();

    // 刷新状态
    void flush();

    // 是否已完成
    boolean isCompleted();
}

commit():提交事务

java 复制代码
void commit(TransactionStatus status) throws TransactionException;

PlatformTransactionManager 接口仅定义方法声明,通用实现逻辑在抽象类 AbstractPlatformTransactionManager 中,核心流程如下:

java 复制代码
// 摘自 AbstractPlatformTransactionManager
public final void commit(TransactionStatus status) throws TransactionException {
    // 1. 校验:事务已完成则抛异常(避免重复提交/回滚)
    if (status.isCompleted()) {
        throw new IllegalTransactionStateException(
            "Transaction is already completed - do not call commit or rollback more than once per transaction");
    }

    // 2. 若标记为"仅回滚",则执行回滚而非提交
    if (status.isRollbackOnly()) {
        rollback(status);
        return;
    }

    // 3. 执行实际提交逻辑(由具体实现类重写doCommit)
    try {
        doCommit(status);
    } catch (TransactionException ex) {
        // 提交失败:尝试回滚,避免数据不一致
        try {
            doRollback(status);
        } catch (RuntimeException | Error rbex) {
            logger.error("Commit exception overridden by rollback exception", ex);
            throw rbex; // 优先抛出回滚异常,避免覆盖原提交异常
        }
        throw ex;
    } finally {
        // 4. 清理事务资源(解绑线程、释放连接等)
        cleanupAfterCompletion(status);
    }
}

rollback():回滚事务

java 复制代码
void rollback(TransactionStatus status) throws TransactionException;

同样,通用实现逻辑在 AbstractPlatformTransactionManager 中:

java 复制代码
// 摘自 AbstractPlatformTransactionManager
public final void rollback(TransactionStatus status) throws TransactionException {
    // 1. 校验:事务已完成则抛异常
    if (status.isCompleted()) {
        throw new IllegalTransactionStateException(
            "Transaction is already completed - do not call commit or rollback more than once per transaction");
    }

    // 2. 执行实际回滚逻辑(由具体实现类重写doRollback)
    try {
        doRollback(status);
    } catch (RuntimeException | Error ex) {
        throw ex;
    } finally {
        // 3. 清理事务资源
        cleanupAfterCompletion(status);
    }
}

实战示例:Spring Boot 中使用 DataSourceTransactionManager

DataSourceTransactionManager 是 Spring 适配 JDBC 数据源的本地事务管理器,也是 Spring Boot 单数据源场景下的默认事务管理器。

首先需要添加前置依赖,在 pom.xml 引入 Spring Boot JDBC 起步依赖(自动引入事务相关核心包):

xml 复制代码
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<!-- 数据库驱动 -->
<dependency>
    <groupId>com.mysql</groupId>
    <artifactId>mysql-connector-j</artifactId>
    <scope>runtime</scope>
</dependency>

步骤 1:配置事务管理器

Spring Boot 默认自动配置 DataSourceTransactionManager,通过 application.yml/application.properties 配置数据源

yaml 复制代码
# application.yml
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/test_db?useUnicode=true&characterEncoding=utf8&useSSL=false
    username: root
    password: 123456
    driver-class-name: com.mysql.cj.jdbc.Driver
    hikari:
      maximum-pool-size: 10

仅在多数据源或自定义事务配置场景需手动声明

方法一:手动声明配置(Java Config)

java 复制代码
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;

import com.zaxxer.hikari.HikariDataSource;
import javax.sql.DataSource;

@Configuration
// Spring Boot中可省略(自动开启),非Boot环境/自定义AOP时需加
@EnableTransactionManagement  
public class TransactionConfig {

    // 配置数据源(完整可运行的HikariCP配置)
    @Bean
    public DataSource dataSource() {
        HikariDataSource dataSource = new HikariDataSource();
        dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/test_db?useUnicode=true&characterEncoding=utf8&useSSL=false");
        dataSource.setUsername("root");
        dataSource.setPassword("123456");
        dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
        dataSource.setMaximumPoolSize(10);
        return dataSource;
    }

    // 配置本地事务管理器(单数据源)
    @Bean
    public PlatformTransactionManager transactionManager(DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }
}

方法二:XML 配置

xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/tx
        http://www.springframework.org/schema/tx/spring-tx.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd">

    <!-- 1. 配置数据源(以HikariCP为例) -->
    <bean id="dataSource" class="com.zaxxer.hikari.HikariDataSource">
        <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/test_db"/>
        <property name="username" value="root"/>
        <property name="password" value="123456"/>
        <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
    </bean>

    <!-- 2. 配置事务管理器 -->
    <bean id="transactionManager" 
          class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <!-- 3. 配置事务通知(定义事务规则) -->
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <tx:attributes>
            <tx:method name="save*" propagation="REQUIRED"/>
            <tx:method name="update*" propagation="REQUIRED"/>
            <tx:method name="delete*" propagation="REQUIRED"/>
            <tx:method name="transfer*" propagation="REQUIRED" isolation="READ_COMMITTED" timeout="30"/>
            <tx:method name="get*" propagation="SUPPORTS" read-only="true"/>
            <tx:method name="find*" propagation="SUPPORTS" read-only="true"/>
            <tx:method name="*" propagation="SUPPORTS" read-only="true"/>
        </tx:attributes>
    </tx:advice>

    <!-- 4. 配置AOP切面(绑定事务规则到业务方法) -->
    <aop:config>
        <aop:pointcut id="serviceOperation" 
                      expression="execution(* com.example.service.*.*(..))"/>
        <aop:advisor advice-ref="txAdvice" pointcut-ref="serviceOperation"/>
    </aop:config>
</beans>

步骤 2:编程式使用事务管理器(非注解方式)

Dao 层:

java 复制代码
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;

@Repository
public class UserDao {
    private final JdbcTemplate jdbcTemplate;

    // 构造器注入JdbcTemplate
    public UserDao(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }

    // 扣减余额
    public void deductBalance(Long userId, Double amount) {
        String sql = "UPDATE user_account SET balance = balance - ? WHERE user_id = ?";
        jdbcTemplate.update(sql, amount, userId);
    }

    // 增加余额
    public void increaseBalance(Long userId, Double amount) {
        String sql = "UPDATE user_account SET balance = balance + ? WHERE user_id = ?";
        jdbcTemplate.update(sql, amount, userId);
    }
}

Service 层:

java 复制代码
import org.springframework.stereotype.Service;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;

@Service
public class UserService {
    private final PlatformTransactionManager transactionManager;
    private final UserDao userDao;

    // 构造器注入
    public UserService(PlatformTransactionManager transactionManager, UserDao userDao) {
        this.transactionManager = transactionManager;
        this.userDao = userDao;
    }

    public void transferMoney(Long fromUserId, Long toUserId, Double amount) {
        // 1. 定义事务属性:隔离级别、传播行为、超时时间等
        TransactionDefinition def = new DefaultTransactionDefinition() {{
            setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED); // 隔离级别
            setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED); // 传播行为
            setTimeout(30); // 超时时间(秒)
        }};
        
        //  Spring 提供的 Builder 写法
        //TransactionDefinition def = DefaultTransactionDefinition.builder()
        //	.isolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED)
        //	.propagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED)
        //	.timeout(30)
        //	.build();

        // 2. 获取事务状态(开启事务)
        TransactionStatus status = transactionManager.getTransaction(def);

        try {
            // 3. 执行业务逻辑(扣减A账户,增加B账户)
            userDao.deductBalance(fromUserId, amount);
            userDao.increaseBalance(toUserId, amount);

            // 4. 提交事务
            transactionManager.commit(status);
        } catch (Exception e) {
            // 5. 异常回滚
            transactionManager.rollback(status);
            throw new RuntimeException("转账失败", e);
        }
    }
}

步骤 3:注解式使用

Spring 的 @Transactional 注解是对 TransactionManager 的封装,无需手动调用 commit/rollback:

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

@Service
public class UserService {
    private final UserDao userDao;

    public UserService(UserDao userDao) {
        this.userDao = userDao;
    }

    // 注解式事务(底层由DataSourceTransactionManager执行)
    @Transactional(
        isolation = Isolation.READ_COMMITTED,
        propagation = Propagation.REQUIRED,
        timeout = 30
    )
    public void transferMoney(Long fromUserId, Long toUserId, Double amount) {
        userDao.deductBalance(fromUserId, amount);
        userDao.increaseBalance(toUserId, amount);
    }
}

事务隔离级别

事务隔离级别是关系型数据库实现事务隔离性(ACID 中 "I" 的核心)的标准化规则,是数据库系统为平衡并发事务的一致性与系统执行性能,对多个并发执行的事务之间的数据可见性边界所做的约束性规定。它明确了一个事务在执行期间,能够读取到其他并发事务的哪些数据状态,核心用于解决并发事务引发的脏读、不可重复读、幻读等数据一致性问题。

隔离级别枚举值 对应数据库隔离级别 解决的问题 未解决的问题
DEFAULT 数据库默认级别,随数据库而定 - -
READ_UNCOMMITTED 读未提交 无(最低隔离级别) 解决脏读、不可重复读、幻读
READ_COMMITTED 读已提交(MySQL默认) 脏读 解决不可重复读、幻读
REPEATABLE_READ 可重复读(Oracle默认) 解决脏读、不可重复读 幻读(MySQL 已通过 MVCC 基本解决)
SERIALIZABLE 串行化 解决脏读、不可重复读、幻读 无(最高隔离级别)

事务传播行为

事务传播行为是Spring 事务管理框架特有的规则,描述了被 @Transactional 注解修饰的方法在被另一个方法调用时,该方法应如何参与调用方的事务上下文,核心用于解决多方法嵌套调用场景下的事务协同问题。

传播行为枚举值 核心规则 适用场景 关键特点
REQUIRED(默认值) 若当前存在事务则加入,无事务则新建 绝大多数增删改操作 同事务,最常用
SUPPORTS 若当前存在事务则加入,无事务则以非事务方式执行 纯查询操作(get/find/list) 不主动创建事务,复用外层事务避免脏读
REQUIRES_NEW 无论当前是否有事务,都新建独立事务,原有事务会被挂起 独立提交的操作(日志记录、消息发送、积分记录) 事务独立,主事务回滚不影响子事务执行结果
NESTED 若当前有事务则在嵌套事务执行,无事务则新建;子事务回滚不影响主事务 批量操作(批量下单、分批处理数据) 基于数据库保存点,主事务回滚子事务必回滚
MANDATORY 必须在已存在的事务中执行,无事务则抛出异常 强制要求事务的核心资金操作 无事务直接报错,极少用
NOT_SUPPORTED 以非事务方式执行,若当前有事务则挂起当前事务 长耗时非核心查询(避免占用事务资源超时) 完全不参与事务,有事务也会暂停
NEVER 以非事务方式执行,若当前有事务则抛出异常 强制禁止事务的操作(纯缓存读写) 有事务直接报错,极少用

TransactionManager 常见问题

问题1:长事务锁表/死锁

场景:

java 复制代码
@Transactional
public void processLargeOrder(Order order) {
    // 1. 查询并锁定库存记录(行锁/表锁)
    Inventory inventory = inventoryDao.selectForUpdate(order.getProductId());
    
    // 2. 复杂计算逻辑(耗时5秒)
    BigDecimal amount = complexCalculation(order);
    
    // 3. 调用外部系统(网络IO,耗时10秒)
    paymentService.charge(order.getUserId(), amount);
    
    // 4. 更新库存
    inventory.setQuantity(inventory.getQuantity() - order.getQuantity());
    inventoryDao.update(inventory);
}

问题:事务持有锁25秒,其他线程被阻塞,导致死锁或超时

解决方案:

java 复制代码
@Service
public class OrderServiceV2 {
    
    @Autowired
    private TransactionTemplate transactionTemplate;
    
    // 方案1:拆分事务,尽早释放锁
    public void processLargeOrder(Order order) {
        // 第一步:快速锁定并扣减库存(短事务)
        boolean inventorySuccess = transactionTemplate.execute(status -> {
            Inventory inventory = inventoryDao.selectForUpdate(order.getProductId());
            if (inventory.getQuantity() < order.getQuantity()) {
                status.setRollbackOnly();
                return false;
            }
            inventoryDao.deductQuantity(order.getProductId(), order.getQuantity());
            return true;
        });
        
        if (!inventorySuccess) {
            throw new InventoryException("库存不足");
        }
        
        // 第二步:复杂计算(无事务)
        BigDecimal amount = complexCalculation(order);
        
        // 第三步:支付(独立事务)
        try {
            paymentService.charge(order.getUserId(), amount);
        } catch (Exception e) {
            // 支付失败,恢复库存(需要幂等)
            restoreInventory(order.getProductId(), order.getQuantity());
            throw e;
        }
        
        // 第四步:记录日志等非核心操作
        asyncLogService.logOrder(order, amount);
    }
    
    // 方案2:使用乐观锁
    @Transactional
    public void processOrderWithOptimisticLock(Order order) {
        // 使用版本号控制
        int rows = inventoryDao.deductWithVersion(
            order.getProductId(), 
            order.getQuantity(),
            currentVersion);
            
        if (rows == 0) {
            // 版本冲突,重试或抛异常
            throw new OptimisticLockException("库存已被修改,请重试");
        }
        
        // 后续操作...
    }
}

问题2:连接池耗尽

场景:

java 复制代码
@Transactional
public void batchProcess(List<Data> dataList) {
    for (Data data : dataList) {
        // 每个循环都调用其他服务方法(开启新事务传播)
        someService.process(data);
    }
    // 事务持有连接直到循环结束
}

解决方案:

java 复制代码
@Service
public class BatchProcessor {
    
    @Autowired
    private DataService dataService;
    
    // 方案1:非事务批量处理
    @Transactional(propagation = Propagation.NOT_SUPPORTED)  // 挂起事务
    public void batchProcess(List<Data> dataList) {
        int batchSize = 100;
        for (int i = 0; i < dataList.size(); i += batchSize) {
            List<Data> batch = dataList.subList(i, Math.min(i + batchSize, dataList.size()));
            
            // 每个批次独立事务
            processBatch(batch);
            
            // 释放连接
            EntityManagerHelper.clear();  // JPA清理上下文
        }
    }
    
    @Transactional(propagation = Propagation.REQUIRES_NEW, timeout = 30)
    private void processBatch(List<Data> batch) {
        for (Data data : batch) {
            dataService.process(data);
        }
    }
    
    // 方案2:使用连接泄漏检测
    @Configuration
    public class DataSourceConfig {
        @Bean
        public DataSource dataSource() {
            HikariDataSource ds = new HikariDataSource();
            ds.setMaximumPoolSize(20);
            ds.setLeakDetectionThreshold(10000);  // 10秒连接泄漏检测
            ds.setConnectionTimeout(30000);  // 30秒连接超时
            return ds;
        }
    }
}

问题3:分布式事务不一致

场景:

java 复制代码
@Transactional
public void placeDistributedOrder(Order order) {
    // 本地数据库
    orderDao.save(order);
    
    // 远程服务调用
    inventoryService.deduct(order.getProductId(), order.getQuantity());  // HTTP调用
    
    // 另一个远程服务
    couponService.useCoupon(order.getCouponId());  // RPC调用
}

解决方案:

java 复制代码
@Service
public class DistributedOrderService {
    
    // 方案1:最终一致性 + 本地消息表
    @Transactional
    public void placeOrderWithLocalMessage(Order order) {
        // 1. 保存订单(本地事务)
        orderDao.save(order);
        
        // 2. 记录本地消息(同一个事务)
        Message message = new Message();
        message.setType("INVENTORY_DEDUCT");
        message.setContent(JSON.toJSONString(order));
        messageDao.save(message);
        
        // 3. 发送消息到MQ(事务提交后)
        TransactionSynchronizationManager.registerSynchronization(
            new TransactionSynchronization() {
                @Override
                public void afterCommit() {
                    // 异步发送消息
                    mqProducer.send(message);
                }
            }
        );
    }
    
    // 方案2:TCC模式
    public void placeOrderWithTCC(Order order) {
        // Try阶段
        inventoryService.tryDeduct(order.getProductId(), order.getQuantity());
        couponService.tryUse(order.getCouponId());
        
        try {
            // Confirm阶段
            orderDao.save(order);
            inventoryService.confirmDeduct(order.getProductId());
            couponService.confirmUse(order.getCouponId());
        } catch (Exception e) {
            // Cancel阶段
            inventoryService.cancelDeduct(order.getProductId());
            couponService.cancelUse(order.getCouponId());
            throw e;
        }
    }
    
    // 方案3:使用Seata分布式事务
    @GlobalTransactional  // Seata注解
    public void placeOrderWithSeata(Order order) {
        orderDao.save(order);
        inventoryFeignClient.deduct(order.getProductId(), order.getQuantity());
        couponFeignClient.use(order.getCouponId());
    }
}

问题4:部分更新问题

场景:

java 复制代码
@Transactional
public void updateUserProfile(User user) {
    userDao.updateBasicInfo(user);     // 成功
    userDao.updatePreferences(user);   // 失败,但第一个更新已提交?
    userDao.updateStatistics(user);    // 不会执行
}

解决方案:

java 复制代码
@Service
public class UserService {
    
    // 方案1:使用声明式事务,默认会全部回滚
    @Transactional(rollbackFor = Exception.class)
    public void updateUserProfile(User user) {
        // 所有操作在一个事务中
    }
    
    // 方案2:手动控制保存点
    @Transactional
    public void updateUserWithSavepoint(User user) {
        Object savepoint = TransactionAspectSupport.currentTransactionStatus().createSavepoint();
        
        try {
            userDao.updateBasicInfo(user);
        } catch (Exception e) {
            // 只回滚到保存点
            TransactionAspectSupport.currentTransactionStatus().rollbackToSavepoint(savepoint);
            log.warn("基本信息更新失败,已回滚", e);
        }
        
        try {
            userDao.updatePreferences(user);
        } catch (Exception e) {
            log.error("偏好设置更新失败,但基本信息已提交", e);
            // 可以继续执行其他操作
        }
        
        // 继续执行...
    }
    
    // 方案3:使用补偿事务
    public void updateUserWithCompensation(User user) {
        // 记录原始状态
        User original = userDao.findById(user.getId());
        
        try {
            userDao.updateBasicInfo(user);
            userDao.updatePreferences(user);
            userDao.updateStatistics(user);
        } catch (Exception e) {
            // 执行补偿操作
            compensateUserUpdate(original);
            throw e;
        }
    }
}

问题5:事务注解滥用

场景:

java 复制代码
@Service
public class ProductService {
    
    @Transactional  // ❌ 滥用:查询方法不需要事务
    public Product getProduct(Long id) {
        return productDao.findById(id);
    }
    
    @Transactional  // ❌ 滥用:简单操作过度设计
    public void updateProductName(Long id, String name) {
        productDao.updateName(id, name);
    }
    
    @Transactional(propagation = Propagation.REQUIRES_NEW)  // ❌ 不必要的独立事务
    public void logAccess(Long productId) {
        accessLogDao.log(productId);
    }
}

解决方案:

java 复制代码
@Service
public class ProductServiceOptimized {
    
    // ✅ 正确:查询方法使用SUPPORTS
    @Transactional(propagation = Propagation.SUPPORTS, readOnly = true)
    public Product getProduct(Long id) {
        return productDao.findById(id);
    }
    
    // ✅ 正确:简单更新使用默认事务
    @Transactional
    public void updateProductName(Long id, String name) {
        productDao.updateName(id, name);
    }
    
    // ✅ 正确:日志记录不需要事务,或使用NOT_SUPPORTED
    @Transactional(propagation = Propagation.NOT_SUPPORTED)
    public void logAccess(Long productId) {
        // 如果日志失败,不应影响主业务
        try {
            accessLogDao.log(productId);
        } catch (Exception e) {
            log.error("记录访问日志失败", e);
        }
    }
    
    // ✅ 事务配置规范
    @Transactional(
        propagation = Propagation.REQUIRED,  // 默认
        isolation = Isolation.READ_COMMITTED,  // 明确隔离级别
        timeout = 30,  // 设置超时
        rollbackFor = {BusinessException.class, RuntimeException.class},  // 明确回滚异常
        readOnly = false
    )
    public void complexBusiness() {
        // 复杂业务逻辑
    }
}

问题6:循环依赖与自调用

场景:

java 复制代码
@Service
public class OrderService {
    
    @Autowired
    private OrderService orderService;  // 循环依赖
    
    @Transactional
    public void placeOrder(Order order) {
        validateOrder(order);      // 事务生效
        processPayment(order);     // 事务生效
        updateInventory(order);    // 事务生效
        sendNotification(order);   // ❌ 事务失效(自调用)
    }
    
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    private void sendNotification(Order order) {
        // AOP代理失效,事务注解无效
        notificationDao.save(new Notification(order));
    }
}

解决方案:

java 复制代码
@Service
public class OrderServiceFixed {
    
    @Autowired
    private NotificationService notificationService;  // 拆分为独立服务
    
    @Autowired
    private OrderService selfProxy;  // 自注入解决自调用
    
    @Transactional
    public void placeOrder(Order order) {
        validateOrder(order);
        processPayment(order);
        updateInventory(order);
        
        // 方案1:调用其他服务
        notificationService.sendOrderNotification(order);
        
        // 方案2:通过代理调用自身方法
        selfProxy.sendNotificationInternal(order);
    }
    
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void sendNotificationInternal(Order order) {
        notificationDao.save(new Notification(order));
    }
}

@Service
public class NotificationService {
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void sendOrderNotification(Order order) {
        notificationDao.save(new Notification(order));
    }
}

// 方案3:使用编程式事务
@Service
public class OrderServiceProgrammatic {
    
    @Autowired
    private TransactionTemplate transactionTemplate;
    
    public void placeOrder(Order order) {
        // 主事务
        transactionTemplate.execute(status -> {
            validateOrder(order);
            processPayment(order);
            updateInventory(order);
            return null;
        });
        
        // 独立事务发送通知
        transactionTemplate.execute(new TransactionCallbackWithoutResult() {
            @Override
            protected void doInTransactionWithoutResult(TransactionStatus status) {
                notificationDao.save(new Notification(order));
            }
        });
    }
}

问题7:事务难以监控和排查

解决方案:

java 复制代码
@Aspect
@Component
@Slf4j
public class TransactionMonitorAspect {
    
    private ThreadLocal<TransactionContext> context = new ThreadLocal<>();
    
    @Around("@annotation(transactional)")
    public Object monitorTransaction(ProceedingJoinPoint joinPoint, 
                                    Transactional transactional) throws Throwable {
        
        TransactionContext ctx = new TransactionContext();
        ctx.setStartTime(System.currentTimeMillis());
        ctx.setMethod(joinPoint.getSignature().toShortString());
        ctx.setPropagation(transactional.propagation());
        context.set(ctx);
        
        MDC.put("txId", UUID.randomUUID().toString());
        
        log.info("事务开始: {}, 传播行为: {}", ctx.getMethod(), ctx.getPropagation());
        
        try {
            Object result = joinPoint.proceed();
            ctx.setSuccess(true);
            return result;
        } catch (Exception e) {
            ctx.setSuccess(false);
            ctx.setError(e.getMessage());
            
            // 检查是否需要回滚
            for (Class<?> rollbackFor : transactional.rollbackFor()) {
                if (rollbackFor.isAssignableFrom(e.getClass())) {
                    log.warn("事务回滚: {}, 原因: {}", ctx.getMethod(), e.getMessage());
                    throw e;
                }
            }
            log.info("事务提交(异常但无需回滚): {}, 异常: {}", ctx.getMethod(), e.getMessage());
            throw e;
        } finally {
            ctx.setEndTime(System.currentTimeMillis());
            long duration = ctx.getEndTime() - ctx.getStartTime();
            
            if (duration > 5000) {  // 5秒以上记录警告
                log.warn("长事务警告: {}, 耗时: {}ms", ctx.getMethod(), duration);
            }
            
            // 发送到监控系统
            Metrics.counter("transaction.count",
                "method", ctx.getMethod(),
                "success", String.valueOf(ctx.isSuccess()),
                "duration", String.valueOf(duration)
            ).increment();
            
            context.remove();
            MDC.remove("txId");
        }
    }
    
    @Data
    private static class TransactionContext {
        private String method;
        private Propagation propagation;
        private long startTime;
        private long endTime;
        private boolean success;
        private String error;
    }
}

@EnableTransactionManagement、@Transactional 与 TransactionManager的区别与联系

各核心组件的定义与核心职责

TransactionManager(事务管理器)

TransactionManager 是 Spring 事务管理的底层执行引擎,其顶层抽象为 org.springframework.transaction.PlatformTransactionManager 接口(Spring 事务抽象规范的核心),定义了事务生命周期的三大核心操作,构成事务管控的底层能力:

TransactionStatus getTransaction(TransactionDefinition definition):根据事务定义获取或开启新事务;

void commit(TransactionStatus status):提交事务(仅当事务状态为正常时执行);

void rollback(TransactionStatus status):回滚事务(当业务异常或事务标记为回滚时执行)。

为适配不同持久化技术,Spring 提供了专属的 TransactionManager 实现类,不同持久化技术对应专属的 TransactionManager 实现。

@Transactional(事务声明注解)

@Transactional 是 Spring 声明式事务的规则声明载体,其核心价值是 "声明需要事务管控的方法及事务行为规则",而非直接执行事务操作。该注解基于 Spring AOP 动态代理实现,最终通过调用 TransactionManager 完成事务的生命周期管控,核心作用包括:

  • 标记范围:标注在 Spring 容器管理的 Bean 类 / 方法上(类级别标注时,所有 public 方法均继承事务规则;方法级别标注优先级更高);
  • 定义规则:通过注解属性精细化配置事务行为(符合 ACID 特性),核心属性及作用如下:
    propagation:事务传播行为,定义多方法嵌套调用时的事务嵌套规则;
    isolation:事务隔离级别,解决并发场景下的脏读、不可重复读、幻读问题;
    rollbackFor/noRollbackFor:精准控制事务回滚 / 不回滚的异常类型(默认仅对运行时异常回滚);
    timeout:事务超时时间,超出设定时长后自动回滚,避免长事务占用资源。

@EnableTransactionManagement(事务管理启用注解)

@EnableTransactionManagement 是 Spring 声明式事务功能的基础设施开关注解,其核心作用是触发 Spring 容器中事务相关 AOP 组件的注册,使 @Transactional 注解能被识别并关联到 TransactionManager。该注解底层通过 TransactionManagementConfigurationSelector 导入两个核心配置类,完成事务 AOP 机制的初始化:

AutoProxyRegistrar:启用 Spring AOP 的自动代理机制,为标注 @Transactional 的 Bean 生成动态代理对象;

ProxyTransactionManagementConfiguration:注册 TransactionInterceptor(事务拦截器)------ 作为连接 @Transactional 和 TransactionManager 的核心桥梁,包含 TransactionAttributeSource(事务属性解析器,解析 @Transactional 的配置规则)和 TransactionManagerResolver(事务管理器解析器,匹配对应的 TransactionManager 实例)。

三者的协同生效约束

三者的协同工作需满足严格的 "存在性约束" 和 "适配性约束",缺一不可,否则事务管控会失效。

存在性约束:

  • 无 @EnableTransactionManagement:Spring 不会注册事务拦截器等 AOP 组件,@Transactional 仅为 "无效注解",无法触发任何事务管控逻辑;
  • 无 TransactionManager:事务拦截器能识别 @Transactional,但无底层执行引擎,运行时会抛出 NoTransactionManagerFoundException;
  • 无 @Transactional:TransactionManager 无触发条件,无法主动介入方法执行,业务操作无事务保护(即使 @EnableTransactionManagement 已开启)。

适配性约束:

  • TransactionManager 类型适配:TransactionManager 必须与持久化技术匹配(如 JDBC 操作使用 JpaTransactionManager 会导致事务失效);
  • @Transactional 代理生效条件:标注 @Transactional 的方法需满足 Spring AOP 代理生效规则,否则事务拦截器无法捕获注解:
    • 方法权限:必须为 public 修饰(JDK 动态代理仅能代理接口的 public 方法;Spring 规范中 CGLIB 代理也仅解析 public 方法的 @Transactional 注解);
    • 调用方式:禁止内部调用(同一类中方法互相调用时,调用方为目标对象本身,而非代理对象,无法触发事务拦截器链);
    • Bean 管控:目标对象必须是 Spring 容器管理的 Bean(需通过 @Service/@Component 等注解声明,或 @Bean 配置,且通过 @Autowired/ApplicationContext.getBean() 获取)------ 仅容器内的 Bean 会被生成代理对象,是事务拦截的基础。

代码示例

配置类:启用事务 + 注册事务管理器

java 复制代码
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;

import javax.sql.DataSource;

@Configuration
// 启用声明式事务,proxyTargetClass = true 强制使用CGLIB代理(适配非接口类)
@EnableTransactionManagement(proxyTargetClass = true)
public class TransactionConfig {

    // 注册符合Spring抽象规范的事务管理器(适配JDBC/MyBatis)
    @Bean
    public PlatformTransactionManager transactionManager(DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }
}

业务类:声明事务并绑定执行引擎

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

@Service
public class OrderService {

    private final OrderMapper orderMapper;
    private final InventoryMapper inventoryMapper;

    // 构造器注入(Spring 推荐的依赖注入方式,保证严谨性)
    public OrderService(OrderMapper orderMapper, InventoryMapper inventoryMapper) {
        this.orderMapper = orderMapper;
        this.inventoryMapper = inventoryMapper;
    }

    // 声明式事务:指定传播行为、回滚规则,绑定默认TransactionManager
    @Transactional(
        propagation = Propagation.REQUIRED, // 默认传播行为,嵌套事务合并
        isolation = Isolation.READ_COMMITTED, // 读已提交,避免脏读
        rollbackFor = Exception.class, // 所有异常均回滚
        timeout = 30 // 事务超时时间30秒
    )
    public void createOrder(Long productId, Integer count) {
        // 扣减库存(事务内操作1)
        inventoryMapper.decreaseStock(productId, count);
        // 创建订单(事务内操作2)
        orderMapper.createOrder(productId, count);
        // 任一操作异常,TransactionManager会回滚所有操作
    }
}
相关推荐
郝学胜-神的一滴2 小时前
Linux系统编程:深入理解读写锁的原理与应用
linux·服务器·开发语言·c++·程序人生
Larry_Yanan2 小时前
Qt多进程(十一)Linux下socket通信
linux·开发语言·c++·qt
Hello.Reader2 小时前
PyFlink JAR、Python 包、requirements、虚拟环境、模型文件,远程集群怎么一次搞定?
java·python·jar
计算机学姐2 小时前
基于SpringBoot的汽车租赁系统【个性化推荐算法+数据可视化统计】
java·vue.js·spring boot·后端·spring·汽车·推荐算法
七夜zippoe2 小时前
分布式事务解决方案 2PC 3PC与JTA深度解析
java·分布式事务·cap·2pc·3pc·jta
我是人✓2 小时前
Spring IOC入门
java·数据库·spring
好好研究2 小时前
SpringBoot小案例打包执行流程
java·spring boot·后端
代码游侠2 小时前
学习笔记——ESP8266 WiFi模块
服务器·c语言·开发语言·数据结构·算法
行者962 小时前
Flutter跨平台开发适配OpenHarmony:进度条组件的深度实践
开发语言·前端·flutter·harmonyos·鸿蒙