JPA Hibernate@Transactional异常场景:回滚实体且保留日志
解决方案
方法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); } }
- 在原服务类中注入
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
相关产品推荐
相关产品推荐

