用复合嵌入式插入JPA实体的顺序(一种包含另一个嵌入式的嵌入式)



我正在研究WebSphere 8.5.5(OpenJPA 2.2.3)中的项目,该项目需要级联创建并通过大型JPA注释的实体模型合并。当合并祖父母在祖父母上打电话给entitymanager(),或者是通过交易的提议,我们遇到了一个非常具体的问题。这是详细信息:

实体映射的相关部分:

  1. Entitya具有一个OneTomany到EntityB
  2. EntityB具有一个OneTomany到EntityC
  3. Entityc具有一个OneTomany到Entityd

都有双向映射。实体A和B具有单列主键。实体C具有一个复合主键,该密钥在实体B的主要密钥B中包含外键B。实体D具有复合密钥,其中包括实体C的复合键C。请参阅下面的映射。

@Entity
@Table(name="TableA")
public class EntityA extends BaseEntity {
    @Id
    @GeneratedValue(strategy=GenerationType.SEQUENCE, generator="TABLE_A_ID_GEN")
    @SequenceGenerator(name="TABLE_A_ID_GEN", sequenceName="TABLE_A_ID", allocationSize=1)
    @Column(name="TABLE_A_ID")
    private Integer id;
    @OneToMany(fetch=FetchType.LAZY, mappedBy="entityA", cascade=CascadeType.ALL)
    private List<EntityB> entityBList;
    ...
}
@Entity
@Table(name="TableB")
public class EntityB extends BaseEntity {
    @Id
    @GeneratedValue(strategy=GenerationType.SEQUENCE, generator="TABLE_B_ID_GEN")
    @SequenceGenerator(name="TABLE_B_ID_GEN", sequenceName="TABLE_B_ID", allocationSize=1)
    @Column(name="TABLE_B_ID")
    private Integer id;
    @ManyToOne(fetch=FetchType.LAZY, cascade=CascadeType.ALL)
    @JoinColumn(name="TABLE_A_ID")
    private EntityA entityA;
    @OneToMany(fetch=FetchType.LAZY, mappedBy="entityB", cascade=CascadeType.ALL)
    private List<EntityC> entityCList;
    ...
}
@Entity
@Table(name="TableC")
public class EntityC extends BaseEntity {
    @EmbeddedId
    private EntityC_PK id = new EntityC_PK();
    @MapsId("entityB_Id")
    @ManyToOne(fetch=FetchType.LAZY, cascade=CascadeType.ALL)
    @JoinColumn(name="TABLE_B_ID")
    private EntityB entityB;
    @OneToMany(fetch=FetchType.LAZY, mappedBy="entityC", cascade=CascadeType.ALL)
    private List<EntityD> entityDList;
    ...
}
@Embeddable
public class EntityC_PK implements BaseComponent {
    @Column(name="TABLE_B_ID", nullable = false, updatable = false)
    private Integer entityB_Id;
    @GeneratedValue(strategy=GenerationType.SEQUENCE, generator="TABLE_C_ID_GEN")
    @SequenceGenerator(name="TABLE_C_ID_GEN", sequenceName="TABLE_C_ID", allocationSize=1)
    @Column(name="TABLE_C_ID")
    private Integer entityC_Id;
    ...
}
@Entity
@Table(name="TABLE_D")
public class EntityD extends BaseEntity {
    @EmbeddedId
    private EntityD_PK id = new EntityD_PK();
    @MapsId("entityC_Id")
    @JoinColumns({
        @JoinColumn(name = "TABLE_B_ID"),
        @JoinColumn(name = "TABLE_C_ID")})
    @ManyToOne(fetch=FetchType.LAZY, cascade=CascadeType.ALL)
    private EntityC entityC;
    ...
}
@Embeddable
public class EntityD_PK implements BaseComponent {
    @Embedded
    private EntityC_PK entityC_Id;
    @GeneratedValue(strategy=GenerationType.SEQUENCE, generator="TABLE_D_ID_GEN")
    @SequenceGenerator(name="TABLE_D_ID_GEN", sequenceName="TABLE_D_ID", allocationSize=1)
    @Column(name="TABLE_D_ID")
    private Integer entity_id;
    ...
}

什么有效:

您可以在实体A上调用EntityManager.persist()(随附所有的孩子),模型将正确级联。

什么不起作用:

如果您实例化a并致电EntityManager.persist(Entitya),然后添加子女,孙子等。以正确的顺序执行插入语句。在单位测试的重复执行中,使事情变得更加混淆插入的顺序并不一致。它通过尝试在实体c。

之前插入实体d而失败。

问题:

我们如何纠正JPA注释以在合并时执行正确的插入顺序(并更新/删除)?

编辑1:插入/删除顺序至关重要,因为数据库通过约束执行外键关系。

让我首先状态(也许我说明显的,对不起),您应该查看您的方案的JPA规格.......嵌入式有时对他们。接下来,您说" entitymanager.create()",但我认为您的意思是。您以后谈论合并,所以也许您的意思是.merge?无论哪种方式,我都建议您坚持。如果您想坚持新实体而不是合并,请坚持。虽然不是非法的,但合并通常用于合并独立实体等。

以此为止,让我掌握您的问题的核心,并为您提供可能有助于您订单的财产。如果您的DDL包含外键约束,则您的文本中没有说明。由于您关心订单,我认为您有这样的约束。如果您这样做,OpenJPA对此约束一无所知,因此,将不知道要适当订购。默认情况下,您不能依赖SQL的顺序,而顺序的随机性正是我所期望的。但是,如果您需要以支持FK约束的方式进行订购,那么您需要允许OpenJPA"了解"您的约束。为此,您需要在persistence.xml文件中设置此属性(或者可以将其设置为JVM自定义属性):

<property name="openjpa.jdbc.SchemaFactory" value="native(ForeignKeys=true)"/>  

此属性允许OpenJPA检查您的模式,因此可以了解您的FK约束。有了这些知识,OpenJPA可以正确订购SQL。

最后,如果您没有FK约束,但是您想以某种方式订购SQL,那么您可能需要使用以下方式:

<property name="openjpa.jdbc.UpdateManager" value="operation-order"/>  

否,我重复不要将这两个属性一起使用。它可能具有奇怪的副作用。请先关注Schemafactory属性,然后如果它没有帮助尝试UpdateManager。该操作订单告诉OpenJPA根据您的实体或换句话说的操作顺序,根据您的实体如何持续下订购SQL。实际上,这可能不会对您的情况过于有用,因为您坚持A并期望其他所有内容都会被级联(OpenJPA可能会持续下一步),但是当涉及B和C时,这是一个废话,它将首先出现)。但是,如果您坚持A,则C,然后B,SQL应按插入A,C,然后使用"操作订单"设置的B进行。

相关内容

最新更新