2016-12-14 64 views
0

我正在处理需要级联创建和通过大型JPA注释实体合并的WebSphere 8.5.5(OpenJPA 2.2.3)中的项目模型。在通过调用Grand-Parent上的EntityManager.merge()或者在事务提交时触发flush来合并grand-children时,我们遇到了一个非常具体的问题。下面是详细信息:插入JPA实体与复合EmbeddedId(包含另一个EmbeddedId的EmbeddedId)的顺序

实体映射的

相关部分:

  1. EntityA具有一对多到EntityB
  2. EntityB具有一对多到EntityC
  3. EntityC具有一对多到EntityD

全部都有双向映射。实体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)(或允许提交事务时的隐式合并)将无法按正确的顺序执行INSERT语句。为了让事情更加混乱,INSERTS的顺序在重复执行单元测试时是不一致的。它未能通过尝试实体C.

之前插入实体d的问题:

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

编辑1: 插入/删除顺序至关重要,因为数据库强制外键关系与约束。

+0

在这里看到我的答案,这可能是有用的:http://stackoverflow.com/questions/39024310/openjpa-nested-onetomany-relationships-merge-issue/39025865#39025865 –

回答

2

让我先说明(也许我在说明显,对不起)您应该查看您的场景的JPA规范.......嵌入式有时对它们有不同的规则。接下来,你声明'EntityManager.create()',但我认为你的意思是.persist?你后来谈论合并,所以也许你的意思是.merge?无论哪种方式,我建议你坚持.persist如果你想坚持新的实体,而不是合并。虽然这不是非法的,但合并通常用于合并分离的实体等。

这样一来,让我了解一下您的问题的核心,并给您一个可能有助于您的订单的财产。如果您的ddl包含外键约束,则您没有在文本中声明。既然你关心的是秩序,我会认为你有这样的约束。如果你这样做,OpenJPA对这个约束一无所知,因此,不知道如何适当地排列事物。默认情况下,您不能依赖于SQL的顺序,并且排序的随机性正是我期望的。但是,如果您需要以支持FK约束的方式进行排序,那么您需要允许OpenJPA“了解”约束条件。为此,您需要在持久性中设置此属性。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设置为“operation-order”的顺序进行。

+0

谢谢希思你的有用答案。我发现更多与您的解决方案完全一致的信息。 [链接](http://openjpa.208410.n2.nabble.com/Inconsistent-execution-order-of-INSERT-statements-using-cascading-persist-td678745.html)。您没有提到的一个选项是使用OpenJPA的@ForeignKey注释来标记哪些关系具有外键并需要特定的插入顺序。 –

+1

谢谢!我故意把它留下(也许我不应该)。 OpenJPA的'@ForeignKey'有两个问题:1)它是合适的。 2)它把开发者的责任放在所有正确的地方(错过一个地方,所有投注都关闭)。在你的情况下,几个班级没什么大不了的,但是在一个大型的应用程序中,它让事情变得更加困难。我不会说不使用'@FK',但我会认真考虑使用一行属性(无代码更改),而不是将'@FK'添加到域模型中的正确位置。祝你好运,享受! –