[英]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()或在事务提交时触发刷新来合并孙子孙时,我们遇到了一个非常特殊的问题。 详细信息如下:
实体映射的相关部分:
全部都有双向映射。 实体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.