带有Hibernate的Spring Data JPA存储库 - persist(sql insert)父实体,但只更新嵌套的子实体

时间:2017-05-06 15:29:13

标签: spring hibernate jpa orm spring-data

我正在使用Spring数据,JPA,Hibernate组合开发Spring Boot应用程序。下面是我正在努力解决在将父实体作为新插入父实体时仅更新某些子实体的预期行为的情况。

  

实体类

@Entity
public class A {
  @Id
  private long id;

  @ManyToOne
  @JoinColumn (name = "B_ID")
  @Cascade ( { CascadeType.ALL } )
  private B b;
}

@Entity
@DynamicUpdate
public class B {
  @Id
  private long id;

  @OneToMany (mappedBy = "b")
  @Cascade ( { CascadeType.ALL } )
  private Set<A> as;

  @ManyToOne
  @JoinColumn (name = "C_ID")
  @Cascade ( { CascadeType.ALL } )
  private C c;
}


@Entity
@DynamicUpdate
public class C {
  @Id
  private long id;

  @OneToMany (mappedBy = "c")
  @Cascade ( { CascadeType.ALL } )
  private Set<B> bs;

  @ManyToOne
  @JoinColumn (name = "D_ID")
  @Cascade ( { CascadeType.ALL } )
  private D d;

  @ManyToOne
  @JoinColumn (name = "E_ID")
  @Cascade ( { CascadeType.ALL } )
  private E e;
}


@Entity
@DynamicUpdate
public class D {
  @Id
  private long id;

  @OneToMany (mappedBy = "d")
  @Cascade ( { CascadeType.ALL } )
  private Set<C> cs;
}

@Entity
@DynamicUpdate
public class E {
  @Id
  private long id;

  @OneToMany (mappedBy = "e")
  @Cascade ( { CascadeType.ALL } )
  private Set<C> cs;
}

以下是我在应用中执行的步骤:

  1. 从回购中拉出B;如果存在,则更新其某些字段值[或]使用字段值创建新的B
  2. 创建A的新实例并将B设置为A
  3. 坚持A(通过调用JpaCrudRepository.save(A))。
  4. 成功部分: 当B已经存在于repo中时,一切正常。这意味着:

    1. 创建了B(和CDE)的新实例,
    2. 这个新创建的B设置为A
    3. A持续成功回购(在DB / repo的所有相应表中插入新行)。
    4. 失败部分:

      1. 现在,当repo中已存在B时,现有B已正确拉出,
      2. BCD保持不变的情况下更新了E的某些字段,
      3. 此更新B已设置为A
      4. 但是,现在尝试保留A时,DE会抛出唯一约束违规。
      5. 所有实体都标有以下内容:

        • ID as auto-generated column (used as PK implicitly),
        • Mapping between entities using the ID column,
        • CascadeType ALL wherever mappings like OneToMany, ManyToOne are applied,
        • Dynamic update annotation.
        

        到目前为止,我可以通过网络收集有关此主题的内容:

        JPA资源库没有显式提供merge()或update()操作,而save()应该涵盖这些操作。如果实体实例已经存在,则此save()操作通过调用merge()或者通过调用persist()(如果它是新的)来工作。进一步挖掘,实体的ID列用于确定它在回购中的存在。如果此ID为null,则将entity确定为new,如果ID不为null,则将其确定为现有。

        因此,在我上面的失败案例中,由于从repo中提取现有的B实体实例,因此它已经具有非空ID。

        所以我目前还不清楚我到底错过了什么。 我尝试在网上找到任何匹配的解决方案,但还没有到达。

        有人可以帮我确定一下我的做法有什么问题吗?

1 个答案:

答案 0 :(得分:0)

找到问题和解决方案。由于A和B被创建为新的,随后C,D,E都被创建为新的,导致违规。因此,为了解决这个问题,我必须从数据库中提取C,D和E中的每个现有实体,并在尝试持久化之前,在新创建的B实体内分别/分层设置它们。

基本上,如果实体嵌套在即将保留的父实体中,则不要为该实体创建新实例。目标是在类似记录已存在时更新。