JPA新增Fiber实体未持久化到数据库问题咨询
JPA新增Fiber记录不入库问题排查
核心问题原因
- 跨事务实体游离导致级联不生效
你在Class1事务中查询得到的Cable、以及从Cable的fibers列表中取出的lastFiber,传入Class2的REQUIRES_NEW新事务后,均处于游离态,不受新事务的EntityManager管理。
Fiber的@ManyToOne仅配置了CascadeType.PERSIST级联规则,该规则仅对未持久化的新Cable实体生效,关联已存在的游离态Cable时不会触发状态同步,最终导致Fiber持久化操作失败。 - 双向关联未同步(非核心但易引发其他问题)
当前是双向关联,Fiber是关联主端负责维护外键,Cable是逆端。仅设置Fiber的cable属性理论上可正常维护外键,但游离态的Cable的fibers集合不会自动同步新增的Fiber,后续操作该集合容易出现状态不一致。 - 代码笔误隐患
Fiber类的@JoinColumn(name = "cable_id)缺少闭合引号,若实际代码存在该问题会导致项目启动失败,若为示例笔误可忽略。
解决方案
方案1:补充级联规则
在Fiber的@ManyToOne注解中增加CascadeType.MERGE,让持久化Fiber时自动将游离态的Cable合并到当前持久化上下文:
@ManyToOne(cascade = {CascadeType.PERSIST, CascadeType.MERGE}) @JoinColumn(name = "cable_id") private Cable cable;
方案2:新事务内重新查询持久态实体(更稳妥)
避免跨事务传递实体,在Class2的方法内重新查询当前事务下的Cable实例再操作,同时注意不要修改原有lastFiber的value值:
@Transactional(propagation = Propagation.REQUIRES_NEW) public void proccess(Cable cableFromDB){ // 重新查询当前事务管理的持久态Cable Cable managedCable = cableRepository.findById(cableFromDB.getId()).orElseThrow(); Fiber lastFiber = managedCable.getFibers().stream() .max(Comparator.comparingInt(Fiber::getValue)) .orElseThrow(); fiberRepository.save(Fiber.builder() .cable(managedCable) .value(lastFiber.getValue() + 1) .build()); }
注意:不要使用
++lastFiber.getValue()写法,该操作会修改原有lastFiber的value属性,若lastFiber处于持久态会触发不必要的更新操作。
内容的提问来源于stack exchange,提问作者user13034249
相关产品推荐
相关产品推荐

