You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

JPA Hibernate@Transactional异常场景:回滚实体且保留日志

解决方案

方法1:给日志操作开启独立事务

核心思路是让日志保存操作在独立于主业务事务的新事务中执行,主事务回滚时不会影响日志事务的提交。

实现步骤:

  1. 把日志操作抽离到单独的LogService类中,给日志方法添加@Transactional(propagation = Propagation.REQUIRES_NEW)注解:
@Service
public class LogService {
    @Autowired
    private LogsRepository logsRepository;

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void logData(String carId, String shedId, int httpStatusCode, String responseMessage) {
        LogDTO log = new LogDTO(carId, shedId, httpStatusCode, responseMessage);
        logsRepository.save(log);
    }
}
  1. 在原服务类中注入LogService,替换原有的日志调用逻辑:
@Service
public class CarService {
    @Autowired
    private LogService logService;
    // 其他依赖注入...

    @Transactional(rollbackOn = {Exception.class, CrudException.class})
    public CarDTO sendToShed(String id, String shedId) {
        CarDTO dbCar = getById(id);
        Shed shed = shedService.getShedById(shedId);
        dbCar.getSheds().add(shed);
        CarDTO updatedCar = patch(dbCar, id);

        try {
            shedService.sendCarToShed(updatedCar, shedId);
        } catch (Exception e) {
            logService.logData(id, shedId, 401, "exception while saving");
            throw new CrudException("Exception while sending project to SmartconX");
        }
        return updatedCar;
    }
}

原理:

Propagation.REQUIRES_NEW会强制开启全新事务,日志方法执行完成后立即提交,不受主业务事务回滚的影响,最终实现业务实体回滚、日志持久化的效果。

方法2:编程式事务手动控制

如果不想拆分服务类,可通过编程式事务手动管理主业务的事务生命周期:

@Service
public class CarService {
    @Autowired
    private TransactionTemplate transactionTemplate;
    @Autowired
    private LogsRepository logsRepository;
    // 其他依赖注入...

    public CarDTO sendToShed(String id, String shedId) {
        return transactionTemplate.execute(status -> {
            // 主业务逻辑在事务内执行
            CarDTO dbCar = getById(id);
            Shed shed = shedService.getShedById(shedId);
            dbCar.getSheds().add(shed);
            CarDTO updatedCar = patch(dbCar, id);

            try {
                shedService.sendCarToShed(updatedCar, shedId);
                return updatedCar;
            } catch (Exception e) {
                // 标记主事务需要回滚
                status.setRollbackOnly();
                // 日志操作脱离主事务直接执行
                LogDTO log = new LogDTO(id, shedId, 401, "exception while saving");
                logsRepository.save(log);
                throw new CrudException("Exception while sending project to SmartconX");
            }
        });
    }
}

原理:

TransactionTemplate的回调方法属于主事务范围,捕获异常后通过status.setRollbackOnly()标记回滚,而日志操作不绑定主事务,会直接持久化到数据库。

注意事项

  • 方法1中要避免同一个类内部调用带REQUIRES_NEW的方法,Spring事务代理基于类/接口实现,内部调用不会触发代理逻辑,导致新事务不生效,必须将日志方法放到独立Bean中。
  • 确保日志表的操作无需和业务事务关联,独立持久化即可。

内容的提问来源于stack exchange,提问作者Arjun Kumar

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.26 13:30:33