【问题标题】:JPA CascadeType's won't work as I expected they willJPA CascadeType 不会像我预期的那样工作
【发布时间】:2022-01-11 11:49:09
【问题描述】:

亲爱的 StackOverflow 社区,您好, 我最近遇到了一个问题,我无法将已保存对象的引用放入其中。我不想保存或更新对象,因为这些对象已预先插入到我们的数据库中。

所以基本上我的情况如下: 我有一个父母,在这种情况下,它是一个 Intake,并且该对象有一个 IntakeTimes 列表,它们被声明为 remainingDoses。有道理,呵呵。

当在剩余剂量上使用 CascadeType.MERGE 时,我实际上能够保存 ChildDisease 对象(我实际保存的最上面的父对象),但是如果在其 Intake 对象上存在具有此类 IntakeTime 的已保存 ChildDisease,我收到“键 'X' 的重复条目 'X'”错误。

在剩余剂量上使用 CascadeType.PERSIST 时,我得到“传递给持久化的分离实体:com.x.x.entity.IntakeTime”

我的模型如下所示:

ChildDisease.java(最高父级)

@Getter
@Setter
@NoArgsConstructor
@Entity
@Table(name="child_disease")
public class ChildDisease extends BaseEntity {

    @ManyToOne(cascade = CascadeType.DETACH)
    @JoinColumn(name="child_Fk")
    private Child child;

    @ManyToOne(cascade = CascadeType.PERSIST)
    @JoinColumn(name="disease_Fk")
    private Disease disease;

    @OneToOne(cascade = CascadeType.PERSIST, orphanRemoval = true, fetch=FetchType.EAGER)
    @JoinColumn(name = "medication_decree_Fk")
    private MedicationDecree medicationDecree;
}

MedicationDecree.java(ChildDisease.java 的直接子代)

@Getter
@Setter
@NoArgsConstructor
@Entity
@Table
public class MedicationDecree extends BaseEntity {

    @Column
    private float consumptionAmount;

    @Column(nullable = false)
    @Enumerated(EnumType.STRING)
    private MeasuringUnit measuringUnit;

    @ManyToOne(cascade = CascadeType.PERSIST)
    @JoinColumn(name="medication_Fk")
    private Medication medication;

    @OneToOne(cascade = CascadeType.PERSIST)
    private Intake intake;

    @ManyToMany(cascade = CascadeType.MERGE)
    private List<IntakeTime> intakeTimes;
}

Intake.java(ChildDisease.java 的直接子代)

@Entity
@Getter
@Setter
@NoArgsConstructor
public class Intake extends BaseEntity {

    @OneToMany(cascade = CascadeType.MERGE)
    public List<IntakeTime> remainingDoses;

    @Column
    public Date date;
}

我期待任何帮助或提示,感谢社区。​​p>

【问题讨论】:

  • 您能否添加完整的代码以重现例如 MERGE 案例,以显示您在保存之前如何初始化所有实体?
  • 不清楚您正在使用哪些 JPA 方法、持久化或合并,以及各种级联类型的操作。在这 4 个实体类型图中也不清楚,哪些是新的,哪些是现有的,或者为什么在不同级别有不同的级联合并操作。 ChildDisease->MedicationDecree-> Intake all 使用 cascade persist,而 MedicationDecree->IntakeTime 和 Intake-> IntakeTime 使用 cascade merge。如果您只使用存在的 ChildDiseases,则合并不会选择 MedicationDecree。如果使用持久化,它不会获取任何引用的 IntakeTime。
  • 听起来您正在创建引用图中现有 IntakeTime 实例的新父级,并调用persist。您必须从当前 EnityManager 上下文中查找那些现有的 IntakeTime 实例,并在 ChildDisease 中使用这些引用。也许在设置这些引用之前坚持 ChildDisease,然后在之后修复/保存 ChildDisease。仅查找它们不会强制更新它们
  • 好的开始,但要重现问题,还需要所有映射器代码和真实数据。在minimal form 中。然后您可能会自己发现问题。
  • 你为什么在intakeTime.getId 值中暂停时调用existsById - 这是在你的测试中从findbyId 切换而来的吗?它们需要添加到集合中并设置在集合中,但您还必须检查 MedicationDecree.intakeTimes 集合。但是您发现了问题 - 您已将 Intake-> IntakeTime 映射为 OneToMany,并且您的表只允许每次 Intake 有一个 IntakeTime。您要么需要更正引用现有 IntakeTime 的现有 Intake,以便只有一个引用它,要么将表/映射更改为 ManyToMany。

标签: java spring jpa cascade


【解决方案1】:

您的错误发生是因为您尝试保存数据库中已经存在的实体(分离),但默认的 JpaRepository 无法识别实体是新的还是旧的,所以它尝试在数据库中创建一个新实体。

您可以通过多种方式解决此问题。其中之一是实现Persistable 接口。 基本上你需要在你的实体上实现Persistable并提供isNew()getId()方法,现在你可以使用如果id字段已经填充返回trueisNew()中的逻辑。

public class Intake extends BaseEntity implements Persistable<UUID> {
   @Override
   public boolean isNew(){
      return this.getId() == null; // or any other logic with which you can identify if it's a new object or not 
   }
}

参考:Persistable SpringDoc

【讨论】:

  • 感谢您提交的答案,但它不适用于我的问题。我已经尝试过并在多个实体中实现了它。键“X”的重复条目“X”仍然存在。还有其他修复吗?你说,有多种方法可以修复它,因为我也认为实现 Persistable 接口不是很一致。我认为最好的方法是使用正确的级联来修复它,但在这种情况下,我几乎尝试了所有级联类型。
猜你喜欢
  • 2012-06-13
  • 2018-11-19
  • 1970-01-01
  • 1970-01-01
  • 2016-03-07
  • 2014-05-16
  • 1970-01-01
  • 2012-07-13
  • 1970-01-01
相关资源
最近更新 更多