繁体   English   中英

具有复合EmbeddedId(包含另一个EmbeddedId的EmbeddedId)的JPA实体的插入顺序

[英]Insert order of JPA entity with compound EmbeddedId (an EmbeddedId that contains another EmbeddedId)

我正在研究WebSphere 8.5.5(OpenJPA 2.2.3)中的一个项目,该项目需要通过大型的JPA带注释的实体模型来级联创建和合并。 通过在祖父母上调用EntityManager.merge()或在事务提交时触发刷新来合并孙子孙时,我们遇到了一个非常特殊的问题。 详细信息如下:

实体映射的相关部分:

  1. EntityA与EntityB具有oneToMany
  2. EntityB与EntityC有一个一对多
  3. EntityC对EntityD有一个toToMany

全部都有双向映射。 实体A和B具有单列主键。 实体C有一个复合主键,其中包括到实体B主键的外键。实体D有一个复合键,其中包括实体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),然后在EntityManager.merge(entityA)时添加子代,孙子代等(或在提交事务时允许隐式合并),它将无法执行以正确的顺序插入语句。 为了使事情更加混乱,在重复执行单元测试时,INSERTS的顺序不一致。 尝试在实体C之前插入实体D失败。

问题:

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

编辑1:插入/删除顺序很关键,因为数据库使用约束强制执行外键关系。

首先让我声明(也许我要说很明显的抱歉),您应该查看针对自己的方案的JPA规范。……嵌入式产品有时对其规则有不同的规定。 接下来,您声明“ EntityManager.create()”,但我认为您的意思是.persist? 您稍后会谈论合并,所以您可能是说.merge? 无论哪种方式,如果您要保留新实体而不是合并,建议您坚持使用.persist。 虽然不是非法的,但合并通常用于合并分离的实体等。

顺便说一句,让我来解答您的问题,为您提供一个可能对您的订单有所帮助的财产。 您没有在文本中声明您的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可能首先坚持使用A,但是当涉及到B和C时,将首先进行处理)。 但是,如果您先保留A,然后是C,然后是B,则SQL应该按照插入顺序依次设置为A,C,然后B的顺序进行。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM