TransactionManager 详解、常见问题、解决方法

一、什么是 TransactionManager

TransactionManage 是Java事务管理的核心接口,负责协调和管理事务的生命周期。

核心接口定义

PlatformTransactionManager是 Spring 事务抽象的核心接口,它采用了 策略模式(Strategy Pattern),为不同的事务管理技术提供了统一的抽象层。

java 复制代码
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package org.springframework.transaction;

import org.springframework.lang.Nullable;

public interface PlatformTransactionManager extends TransactionManager {
    TransactionStatus getTransaction(@Nullable TransactionDefinition definition) throws TransactionException;

    void commit(TransactionStatus status) throws TransactionException;

    void rollback(TransactionStatus status) throws TransactionException;
}

1. getTransaction() - 获取事务

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

定义了事务的所有属性:

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();
}

2. commit() - 提交事务

java 复制代码
void commit(TransactionStatus status) throws TransactionException;
提交逻辑:
java 复制代码
public 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()) {
        // 即使调用commit,也要回滚
        rollback(status);
        return;
    }

    // 3. 执行提交
    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 {
        cleanupAfterCompletion(status);
    }
}

3. rollback() - 回滚事务

java 复制代码
void rollback(TransactionStatus status) throws TransactionException;
回滚逻辑:
java 复制代码
public 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. 执行回滚
    try {
        doRollback(status);
    } catch (RuntimeException | Error ex) {
        throw ex;
    } finally {
        cleanupAfterCompletion(status);
    }
}

二、主要实现类

1. DataSourceTransactionManager(最常用)

java 复制代码
// Spring JDBC事务管理器
<bean id="transactionManager" 
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>

适用场景:单数据源JDBC操作

2. JpaTransactionManager

java 复制代码
// JPA/Hibernate事务管理器
<bean id="transactionManager"
class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory"/>
</bean>

3. JtaTransactionManager

java 复制代码
// 分布式事务管理器(XA协议)
<bean id="transactionManager"
class="org.springframework.transaction.jta.JtaTransactionManager">
<property name="transactionManager" ref="atomikosTransactionManager"/>
</bean>

三、核心配置

基于XML配置

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

<!-- 2. 配置事务通知 -->
<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="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>

<!-- 3. 配置AOP切面 -->
<aop:config>
  <aop:pointcut id="serviceOperation" 
    expression="execution(* com.example.service.*.*(..))"/>
  <aop:advisor advice-ref="txAdvice" pointcut-ref="serviceOperation"/>
</aop:config>

基于Java配置

java 复制代码
@Configuration
@EnableTransactionManagement  // 启用注解事务
public class TransactionConfig {

    @Bean
    public DataSource dataSource() {
        return new HikariDataSource();
    }

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

    @Bean
    public JdbcTemplate jdbcTemplate() {
        return new JdbcTemplate(dataSource());
    }
}

四、事务传播行为(Propagation)

传播行为 说明 使用场景
REQUIRED(默认) 如果当前没有事务,就新建一个事务;如果已存在,就加入 通用业务方法
REQUIRES_NEW 新建事务,如果当前存在事务,则挂起当前事务 日志记录、独立操作
SUPPORTS 支持当前事务,如果当前没有事务,就以非事务方式执行 查询方法
NOT_SUPPORTED 以非事务方式执行,如果当前存在事务,则挂起 调用外部服务
NEVER 以非事务方式执行,如果当前存在事务,则抛出异常 必须非事务执行
MANDATORY 使用当前事务,如果当前没有事务,则抛出异常 必须存在事务
NESTED 如果当前存在事务,则在嵌套事务内执行 复杂业务的分步操作

示例代码

java 复制代码
@Service
public class OrderService {

    @Transactional(propagation = Propagation.REQUIRED)
    public void createOrder(Order order) {
        // 主业务逻辑
        orderDao.save(order);

        // 需要独立事务的操作
        try {
            logService.saveLog(order);  // REQUIRES_NEW
        } catch (Exception e) {
            // 日志失败不影响主事务
        }
    }
}

@Service
public class LogService {

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void saveLog(Order order) {
        // 独立事务,即使主事务回滚,日志也会保存
        logDao.save(createLog(order));
    }
}

五、事务隔离级别(Isolation)

隔离级别 说明 问题解决
DEFAULT 使用数据库默认隔离级别 -
READ_UNCOMMITTED 读未提交 脏读、不可重复读、幻读
READ_COMMITTED 读已提交(Oracle默认) 解决脏读
REPEATABLE_READ 可重复读(MySQL默认) 解决脏读、不可重复读
SERIALIZABLE 串行化 解决所有并发问题

示例配置

java 复制代码
@Transactional(isolation = Isolation.READ_COMMITTED)
public void transferMoney(Long fromId, Long toId, BigDecimal amount) {
    // 需要较高隔离级别的金融操作
    accountDao.subtractBalance(fromId, amount);
    accountDao.addBalance(toId, amount);
}

六、编程式事务管理

使用 TransactionTemplate

java 复制代码
@Service
public class UserService {

    @Autowired
    private TransactionTemplate transactionTemplate;

    @Autowired
    private UserRepository userRepository;

    public void batchUpdateUsers(List<User> users) {
        transactionTemplate.execute(new TransactionCallbackWithoutResult() {
            @Override
            protected void doInTransactionWithoutResult(TransactionStatus status) {
                try {
                    for (User user : users) {
                        userRepository.update(user);

                        // 每100条提交一次
                        if (user.getId() % 100 == 0) {
                            // 手动刷新,但事务仍整体提交
                        }
                    }
                } catch (Exception e) {
                    status.setRollbackOnly();  // 标记回滚
                    throw e;
                }
            }
        });
    }
}

使用 PlatformTransactionManager

java 复制代码
@Service
public class ManualTransactionService {

    @Autowired
    private PlatformTransactionManager transactionManager;

    @Autowired
    private UserRepository userRepository;

    public void complexOperation() {
        // 定义事务属性
        DefaultTransactionDefinition def = new DefaultTransactionDefinition();
        def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
        def.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED);
        def.setTimeout(30);  // 30秒超时

        // 开启事务
        TransactionStatus status = transactionManager.getTransaction(def);

        try {
            // 业务操作
            userRepository.save(user1);
            userRepository.save(user2);

            // 提交事务
            transactionManager.commit(status);
        } catch (Exception e) {
            // 回滚事务
            transactionManager.rollback(status);
            throw e;
        }
    }
}

七、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;
    }
}
相关推荐
程序员佳佳2 小时前
文章标题:彻底抛弃OpenAI官方Key?实测GPT-5.2与Banana Pro(Gemini 3):这才是开发者的终极红利!
开发语言·人工智能·python·gpt·ai作画·api·midjourney
挖矿大亨2 小时前
C++中左移运算符重载
开发语言·c++
廋到被风吹走2 小时前
【Spring】Spring Context 详细介绍
java·后端·spring
CoderCodingNo2 小时前
【GESP】C++五级真题(数论-素数、贪心思想考点) luogu-B4050 [GESP202409 五级] 挑战怪物
开发语言·c++·算法
Kiyra2 小时前
LinkedHashMap 源码阅读
java·开发语言·网络·人工智能·安全·阿里云·云计算
sheji34162 小时前
【开题答辩全过程】以 山林湖泊生态文明建设管控系统为例,包含答辩的问题和答案
java·spring boot
banpu2 小时前
Spring相关
数据库·spring·sqlserver
沐知全栈开发2 小时前
Python3 日期和时间处理详解
开发语言
幽络源小助理2 小时前
SpringBoot兼职发布平台源码 | JavaWeb项目免费下载 – 幽络源
java·spring boot·后端