Java学习-------事务失效

        在 Java 开发中,事务是保证数据一致性和完整性的关键机制,尤其在涉及多步数据库操作的业务场景中不可或缺。然而,在实际开发过程中,事务常常会出现 “失效” 的情况 —— 预期的回滚没有发生,数据出现不一致。

        Java 事务(尤其是基于 Spring 的声明式事务)的正常运作依赖于一系列底层机制和约定,当这些机制被破坏或约定未被遵守时,就会导致事务失效。核心原因可归纳为以下几类:​

(1)事务传播行为配置不当:事务传播行为决定了多个事务方法相互调用时的执行规则,若配置错误(如使用PROPAGATION_SUPPORTS或PROPAGATION_NOT_SUPPORTED),会导致事务无法按预期生效。​

(2)异常处理不当:事务默认只对未捕获的RuntimeException及其子类异常回滚,若异常被手动捕获且未重新抛出,或抛出的是受检异常,会导致事务不回滚。​

(3)方法访问权限问题:Spring 事务基于 AOP 动态代理实现,若目标方法是private、final或static修饰的,代理无法生效,事务自然失效。​

(4)数据源未配置事务管理器:事务的管理依赖于事务管理器,若未为数据源正确配置PlatformTransactionManager,事务注解将无法发挥作用。​

(5)自调用问题:在同一个类中,一个非事务方法调用本类的事务方法时,由于未经过代理对象,事务会失效。​

        通常有以下失效的场景:

        场景一:事务传播行为配置错误​

        场景描述:当一个事务方法调用另一个事务方法时,若传播行为设置不合理,可能导致事务无法正常回滚。例如,在嵌套业务中使用PROPAGATION_REQUIRES_NEW时,内层事务提交后,外层事务回滚不会影响内层;若使用PROPAGATION_SUPPORTS,则方法会跟随当前事务(若不存在则不开启事务)。​

代码示例(错误):​

@Service
public class OrderService {
    @Autowired
    private PaymentService paymentService;

    @Transactional(propagation = Propagation.REQUIRED)
    public void createOrder() {
        // 保存订单
        saveOrder();
        // 调用支付方法,传播行为为SUPPORTS
        paymentService.makePayment();
    }
}

@Service
public class PaymentService {
    @Transactional(propagation = Propagation.SUPPORTS)
    public void makePayment() {
        // 支付逻辑,若当前无事务则不开启事务
        updatePaymentStatus();
        // 模拟异常
        int i = 1 / 0;
    }
}

        问题分析:makePayment方法的传播行为为SUPPORTS,若createOrder的事务未生效(或传播行为不匹配),则支付操作不会在事务中执行,异常发生后无法回滚。​

        解决方法:根据业务需求选择合适的传播行为,如核心业务使用REQUIRED(默认值),确保方法在事务中执行。​

        场景二:异常被捕获但未重新抛出​

        场景描述:事务方法中若手动捕获了异常且未重新抛出,Spring 会认为事务执行成功,不会触发回滚。​

代码示例(错误):​

@Service
public class UserService {
    @Autowired
    private UserMapper userMapper;

    @Transactional
    public void updateUserInfo() {
        try {
            userMapper.updateName(1, "newName");
            // 模拟异常
            int i = 1 / 0;
        } catch (Exception e) {
            // 仅捕获异常未处理
            log.error("更新失败", e);
        }
    }
}

        问题分析:异常被try-catch捕获后,未抛出到外层,Spring 无法感知异常,因此不会回滚updateName操作。​

        解决方法:捕获异常后重新抛出,或通过@Transactional(rollbackFor = Exception.class)指定回滚异常类型,并在外层处理。​

        场景三:方法访问权限问题​

        场景描述:Spring 事务基于 AOP 动态代理实现,若事务方法被private、final或static修饰,代理无法重写该方法,导致事务注解失效。​

代码示例(错误):​

@Service
public class ProductService {
    @Autowired
    private ProductMapper productMapper;

    // private修饰的事务方法
    @Transactional
    private void reduceStock(Long productId, int quantity) {
        productMapper.decreaseStock(productId, quantity);
    }

    public void processOrder(Long productId, int quantity) {
        reduceStock(productId, quantity); // 调用私有方法
    }
}

        ​问题分析:reduceStock为私有方法,Spring 无法生成代理方法,@Transactional注解失效,库存减少操作在异常发生时不会回滚。​

        解决方法:将事务方法修改为public修饰,且避免使用final或static。​

        场景四:未配置事务管理器​

        场景描述:Spring 事务的生效依赖于事务管理器(如DataSourceTransactionManager),若未在配置类中声明事务管理器,@Transactional注解将不起作用。​

        代码示例(错误配置):​

@Configuration
@MapperScan("com.example.mapper")
public class MyBatisConfig {
    @Bean
    public DataSource dataSource() {
        // 配置数据源
        HikariConfig config = new HikariConfig();
        config.setJdbcUrl("jdbc:mysql://localhost:3306/test");
        return new HikariDataSource(config);
    }
}

        问题分析:仅配置了数据源,未配置PlatformTransactionManager,Spring 无法管理事务。​

        解决方法:添加事务管理器配置:​

        场景五:同一类内方法自调用​

        场景描述:在同一个类中,非事务方法直接调用本类的事务方法时,由于调用的是原始对象(非代理对象),事务会失效。​

        代码示例(错误):​

@Service
public class OrderService {
    @Autowired
    private OrderMapper orderMapper;

    public void createOrder() {
        // 非事务方法调用本类事务方法
        saveOrder();
    }

    @Transactional
    public void saveOrder() {
        orderMapper.insert(new Order());
        int i = 1 / 0; // 模拟异常
    }
}

问题分析:createOrder未被@Transactional修饰,调用saveOrder时使用的是当前类实例(非代理对象),事务注解失效,异常发生后订单数据仍会被插入。​

解决方法:​

(1)将事务方法拆分到另一个服务类中,通过依赖注入调用(推荐)。​

(2)自注入代理对象(需开启exposeProxy = true):​

        在这里,给出几个关于事务失效的排查建议​

(1)检查事务注解:确保方法被@Transactional修饰,且注解属性(如rollbackFor、propagation)配置正确。​

(2)验证代理模式:Spring 默认使用 JDK 动态代理(接口),若使用 CGLIB 代理需确保类未被final修饰,且依赖中包含 CGLIB 库。​

(3)查看异常处理:检查事务方法中是否有异常被捕获后未重新抛出,确保异常类型符合rollbackFor配置。​

(4)调试事务日志:在application.properties中添加日志配置logging.level.org.springframework.transaction=DEBUG,观察事务的开启、提交和回滚日志。​

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值