事务回滚,同时记录异常信息

该博客介绍了在Java环境中如何在出现异常时回滚数据库操作并记录异常信息。通过开启新事务,利用HTTP异步请求保存异常详情,确保主程序抛出异常后仍能正确处理。在finally块中,使用`@Transactional`注解和`DataSourceTransactionManager`来控制事务,确保异常信息的插入不受影响。

需求

在发生异常时,回滚对数据库的操作,同时在数据库中插入一条异常信息。

实现原理

开启新的事务,进行异常信息的数据库操作

实现

1、依赖HTTP重新发送请求

在抛出异常前,依赖http,发送一条异步请求,由一个接口保存异常信息,然后主程序再抛出异常。

2、开启新事务,插入异常信息

@Autowired
private DataSourceTransactionManager dstManager;

@Transactional
@Override
public void create(Payment payment) throws Exception {
    try {
        paymentDao.create(payment);
        System.out.println(1 / 0);
    } catch (Exception e) {
        TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
        //此处可添加抛出异常信息,不影响现有逻辑
    } finally {
        // 事物隔离级别,开启新事务,这样会比较安全些。
        DefaultTransactionDefinition def = new DefaultTransactionDefinition();
        def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
        // 获得事务状态
        TransactionStatus transaction = dstManager.getTransaction(def);
        try {
            payment.setSerial("报错啦啦啦啦啦啦");
            paymentDao.create(payment);
            dstManager.commit(transaction);
        } catch (Exception e) {
            dstManager.rollback(transaction);
            logger.error("临时表插入失败:" + e.getMessage(), e);
        }
    }
}

一种实现方式,与上面的代码思想是一样的

@Transactional
@Override
public void create(Payment payment) {
    paymentDao.create1(payment);
    paymentService.newTransCreate(payment);
    System.out.println(1 / 0);
}

@Override
@Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
public void newTransCreate(Payment payment) {
    payment.setSerial("这是一个新的报错信息");
    paymentDao.create2(payment);
}
<think>好的,我现在需要解决用户在Spring事务管理中捕获异常后阻止事务自动回滚的问题。首先,我得回顾一下Spring的事务处理机制。根据之前的引用,Spring默认在遇到未被捕获的RuntimeException时才会回滚事务,而检查型异常(比如Exception的子类)默认不会触发回滚[^1][^3]。用户的问题是在捕获异常后,仍然希望事务回滚,也就是即使发生了异常事务还是能正常提交。 首先,用户可能的情况是,他们在代码中使用了try-catch块捕获了异常,但没有重新抛出,或者捕获的是检查型异常。根据引用[3],如果异常被捕获且未抛出,Spring的AOP代理就无法感知到异常,因此不会触发回滚。但用户现在的情况可能相反,他们捕获了异常,但事务仍然回滚了,所以用户想要阻止这种情况。 不过根据用户的描述,“如何在捕获到异常后阻止事务自动回滚”,可能他们遇到的情况是,即使捕获了异常事务还是回滚了,但根据常规情况,如果捕获了异常并且没有标记回滚事务应该提交。所以可能需要检查用户的具体配置。例如,如果用户捕获的是RuntimeException,并且没有处理,或者配置了rollbackFor某些异常,可能导致事务回滚。 根据引用[4]中的例子,当在catch块中调用TransactionAspectSupport.currentTransactionStatus().setRollbackOnly()时,会显式标记事务回滚。所以,如果用户不想回滚,应该避免这样的操作。同时,需要确保捕获的异常类型不在事务回滚规则中。 用户需要的是在捕获异常后,事务不被回滚。可能的解决方案包括: 1. 调整事务注解的rollbackFor/noRollbackFor属性,指定哪些异常需要回滚,哪些不需要。例如,使用@Transactional(noRollbackFor=SomeException.class),这样即使抛出SomeException,事务也不会回滚。 2. 在捕获异常后,不进行任何触发回滚的操作,例如不调用setRollbackOnly,同时确保该异常类型不在默认或显式指定的回滚异常列表中。 例如,如果用户捕获了一个自定义的检查型异常,而该异常默认不会触发回滚,那么事务应该正常提交。但如果捕获的是RuntimeException,并且没有配置rollbackFor,默认情况下会回滚。因此,用户需要确认捕获的异常类型,以及事务注解的配置。 另外,需要检查事务的传播行为。例如,如果内部方法的事务传播设置为REQUIRES_NEW,可能在内部事务回滚,但外部事务不受影响。不过这可能与用户的问题无关。 总结可能的步骤: - 确认捕获的异常类型是否为RuntimeException或其子类,如果是,默认会回滚,除非显式配置noRollbackFor。 - 在事务注解中添加noRollbackFor属性,排除特定的异常。 - 确保在catch块中没有手动设置回滚。 例如,用户可能在代码中这样: @Transactional public void method() { try { // 业务代码 } catch (Exception e) { // 处理异常,但没有抛出 } } 如果捕获的是Exception,而@Transactional的默认rollbackFor是RuntimeException,那么此时事务不会回滚。但是如果业务代码中抛出了RuntimeException,并且在catch块中被捕获,但没有重新抛出,那么事务应该正常提交,因为Spring没有感知到异常。但根据引用[3],如果异常被捕获且未被重新抛出,AOP代理不会知道异常的存在,所以事务不会回滚。所以这里可能存在矛盾,或者用户的情况可能有其他配置。 或者用户可能配置了rollbackFor=Exception.class,这样任何异常都会触发回滚,即使被捕获。此时需要调整rollbackFor的设置,或者使用noRollbackFor来排除特定异常。 因此,正确的解决方法是: 1. 检查事务注解中的rollbackFor和noRollbackFor属性,确保捕获的异常不在回滚列表中,或者被显式排除。 2. 避免在catch块中手动设置回滚。 3. 如果必须捕获某个特定的RuntimeException,并且不希望事务回滚,可以在事务注解中添加noRollbackFor=ThatException.class。 例如: @Transactional(noRollbackFor = {MyBusinessException.class}) public void process() { try { // 可能抛出MyBusinessException } catch (MyBusinessException e) { // 处理异常事务不会回滚 } } 这样,即使MyBusinessException是RuntimeException的子类,事务也不会回滚,因为它被配置为noRollbackFor。 另外,如果用户希望在某些情况下捕获异常并决定是否回滚,可能需要编程式事务管理,例如使用TransactionTemplate,在catch块中根据条件提交或回滚。 总结,解决方案的关键在于事务注解的配置,以及异常类型的处理。需要确保被捕获的异常不在触发回滚的范围内,或者显式配置不进行回滚。</think>### 如何在捕获异常后阻止Spring事务自动回滚 在Spring事务管理中,若希望在捕获异常后阻止事务自动回滚,需通过**事务配置**和**异常处理逻辑**两方面进行调整。以下是具体实现步骤: --- #### 1. 调整事务注解的`noRollbackFor`属性 通过`@Transactional`注解的`noRollbackFor`参数,明确指定某些异常**不触发回滚**。 **示例**: ```java @Transactional(noRollbackFor = {BusinessException.class}) public void processData() { try { // 业务逻辑(可能抛出BusinessException) } catch (BusinessException e) { // 捕获异常事务不会回滚 } } ``` - **关键点**:若捕获的异常是`RuntimeException`的子类(如自定义业务异常),需显式配置`noRollbackFor`覆盖默认行为[^1][^3]。 --- #### 2. 避免使用默认回滚异常类型 若捕获的是**检查型异常(checked exception)**,如`IOException`,默认不会触发回滚,无需额外配置[^1]。 **示例**: ```java @Transactional public void saveData() { try { // 可能抛出SQLException(检查型异常) } catch (SQLException e) { // 事务正常提交 } } ``` --- #### 3. 禁止在catch块中手动标记回滚 确保在异常处理逻辑中**不调用事务回滚的API**,例如: ```java // 错误做法:显式标记回滚 catch (Exception e) { TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); // 强制回滚 } // 正确做法:不操作事务状态 catch (BusinessException e) { // 仅记录日志,不干预事务 } ``` --- #### 4. 使用编程式事务控制 通过`TransactionTemplate`手动控制事务边界,灵活决定提交或回滚: ```java @Autowired private TransactionTemplate transactionTemplate; public void executeWithCondition() { transactionTemplate.execute(status -> { try { // 业务逻辑 return result; } catch (BusinessException e) { status.setRollbackOnly(); // 按条件回滚(可选) return null; } }); } ``` --- ### 总结方案对比 | 方法 | 适用场景 | 注意事项 | |---------------------|----------------------------|----------------------------------| | `noRollbackFor`配置 | 需要排除特定异常类型 | 需明确异常类,适用于声明式事务 | | 捕获检查型异常 | 默认不触发回滚异常 | 确保异常未被包含在`rollbackFor`中| | 编程式事务 | 复杂条件控制 | 需手动管理事务生命周期 | ---
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值