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

@Retryable结合事务上下文的并发创建实体问题咨询

问题解答

1. 是否需要添加@Transactional(propagation = Propagation.REQUIRES_NEW)?

必须添加,原因如下:

  • 你的方法被外层带@Transactional的方法调用时,默认会沿用外层事务上下文。如果不加REQUIRES_NEW,并发插入抛出DataIntegrityViolationException后,整个外层事务会被标记为回滚状态,后续重试操作会因事务已回滚无法正常执行,也无法查询到其他线程已创建的实体。
  • 配置REQUIRES_NEW后,每次调用(包括重试)都会开启独立的新事务:
    • 第一次插入失败时,仅回滚当前小事务,不会影响外层事务;
    • 重试时,新事务能独立查询数据库,获取到其他线程已创建完成的实体。

2. 是否需要捕获DataIntegrityViolationException并在catch块中获取已创建实体?

不需要,且该操作会与@Retryable逻辑冲突,原因如下:

  • @Retryable的触发条件是指定异常向外抛出,若在方法内部捕获该异常,@Retryable无法感知到异常,也就不会执行重试逻辑。
  • 正确做法是移除try-catch块,让异常直接抛出触发重试:
    • 第一次插入失败,异常抛出触发重试;
    • 重试时方法重新执行,第一步findEntityByUuid(entityUuid)就能查询到其他线程已创建的实体,直接返回即可,无需再执行插入逻辑。

调整后的示例代码

@Retryable(
        retryFor = {DataIntegrityViolationException.class},
        maxAttempts = 2,
        backoff = @Backoff(delay = 100)
)
@Transactional(propagation = Propagation.REQUIRES_NEW)
public T getOrCreateEntity(UUID entityUuid) {
    T entity = findEntityByUuid(entityUuid);
    if (entity != null) {
        return entity;
    }

    log.info("Creating entity with UUID {}", entityUuid);
    T newEntity = createNewEntity(entityUuid);
    return repository.save(newEntity);
}

若坚持保留catch块(比如不想依赖重试机制,直接在异常时查询返回),则可以去掉@Retryable注解,因为此时异常已被内部处理,重试不会生效。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 12:45:59