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

标注@Transactional仍触发LazyInitializationException:事务未传播?

问题:事务传播异常导致延迟加载失败(测试环境正常,生产环境失效)

标注了@Transactional的方法,事务未在嵌套方法中传播,引发LazyInitializationException。奇怪的是,通过@SpringBootTest进行集成测试时延迟加载正常,但Spring Boot 3.3.2部署环境中处理HTTP请求时却失效。

实体映射

@Entity
public class VtBsoEntity {

  @Id
  private UUID id;

  @OneToOne(fetch = FetchType.LAZY)
  @JoinColumn(name = "arriving_train_id", referencedColumnName = "id")
  private VtTrainEntity arrivingTrain;

  @OneToOne(fetch = FetchType.LAZY)
  @JoinColumn(name = "leaving_train_id", referencedColumnName = "id")
  private VtTrainEntity leavingTrain;

  // 其他字段与方法
}

@Entity
public class VtTrainEntity {
  
  @Id
  private UUID id;

  private Integer trainNo;

  // 其他字段与方法
}

REST控制器入口

@RestController
public class TemplateMetadataController {

  private final TemplateMetadataService service;

  public TemplateMetadataController(TemplateMetadataService service) {
    this.service = service;
  }

  public ResponseEntity<List<GetTemplateMetadataDto>> findAllTemplates() {
    final List<TemplateMetadataModel> templates = service.findAll(); // 堆栈跟踪中的失败点
    // 后续逻辑
    return ResponseEntity.ok(...);
  }
  // 其他方法
}

TemplateMetadataService代码

@Service
public class TemplateMetadataService {

  private final NumberCalculatorService numberCalculatorService;

  public TemplateMetadataService(NumberCalculatorService numberCalculatorService) {
    this.numberCalculatorService = numberCalculatorService;
  }

  @Transactional(isolation = Isolation.READ_COMMITTED, readOnly = true)
  public List<TemplateMetadataModel> findAll() {
    // 查询逻辑
    return mapToModel();
  }

  private TemplateMetadataModel mapToModel(TemplateMetadataEntity entity) {
    // 映射逻辑
    numberCalculatorService.getNumberModel(entity); // 堆栈跟踪中的失败点
    // 返回模型
    return ...;
  }

}

NumberCalculatorService代码

@Service
public class NumberCalculatorService {

  private final VtBsoEntityRepository vtBsoEntityRepository;

  public NumberCalculatorService(VtBsoEntityRepository vtBsoEntityRepository) {
    this.vtBsoEntityRepository = vtBsoEntityRepository;
  }

  public AggregateBsoNumberModel getNumberModel(TemplateMetadataEntity entity) {

    List<VtBsoEntity> bsoList = vtBsoEntityRepository
        .findAllByMuWoIdAndActive(entity.getId(), true);

   bsoList.getFirst().getArrivingTrain(); // LazyInitializationException,无会话

   // 后续逻辑
   return ...;
  }

}

问题分析:为什么NumberCalculatorService中持久化上下文丢失?

1. 测试与生产环境的事务上下文差异

测试时,@SpringBootTest会通过TransactionalTestExecutionListener为整个测试方法创建全局事务,EntityManager在测试全程保持打开状态,所有DAO操作共享同一个持久化上下文,延迟加载自然正常。而部署环境中,只有TemplateMetadataService.findAll()方法带有事务,若事务上下文未正确传递到NumberCalculatorService.getNumberModel(),会导致vtBsoEntityRepository的查询脱离原事务,查询完成后EntityManager立即关闭,实体变为游离态,触发延迟加载异常。

2. 事务传播的隐性失效

尽管默认传播行为是Propagation.REQUIRED(加入当前事务),但以下情况会导致传播失效:

  • NumberCalculatorService未通过Spring容器注入(比如手动new实例),事务代理未生效;
  • AOP切面优先级冲突,事务切面未覆盖跨Bean调用逻辑;
  • Spring Boot 3.3.2的事务管理器配置调整,导致PlatformTransactionManager未正确绑定JPA的EntityManagerFactory,事务上下文无法跨Bean传递。

3. 事务提前提交或异步调用

若TemplateMetadataService.findAll()方法中,调用mapToModel()前执行了EntityManager.flush()等操作,或mapToModel()的调用处于异步线程中,会导致原事务提前关闭或事务上下文无法传递到异步线程,使得getNumberModel()中的实体变为游离态。

解决方案

  • 给NumberCalculatorService.getNumberModel()添加@Transactional(propagation = Propagation.REQUIRED, readOnly = true)注解,强制该方法加入当前事务,确保查询的实体处于托管状态;
  • 检查NumberCalculatorService的注入方式,确保由Spring容器管理,避免手动实例化;
  • 验证生产环境的事务管理器配置,确保JpaTransactionManager正确关联EntityManagerFactory;
  • 排查是否存在异步调用,若有则使用@Transactional(propagation = Propagation.REQUIRES_NEW)或手动传递事务上下文。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.19 12:18:11