gpt4 book ai didi

java - 使用复合EmbeddedId(包含另一个EmbeddedId的EmbeddedId)插入JPA实体的顺序

转载 作者:行者123 更新时间:2023-12-02 03:10:24 25 4
gpt4 key购买 nike

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

实体映射的相关部分:

  1. EntityA 有一个到 EntityB 的 oneToMany
  2. EntityB 有一个到 EntityC 的 oneToMany
  3. EntityC 有一个到 EntityD 的 oneToMany

全部都有双向映射。实体 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 语句。更令人困惑的是,插入的顺序在单元测试的重复执行中并不一致。尝试在实体 C 之前插入实体 D 失败。

问题:

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

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

最佳答案

首先让我声明(也许我说的是显而易见的事实,抱歉)您应该针对您的场景查看 JPA 规范......嵌入有时对它们有不同的规则。接下来,您声明“EntityManager.create()”,但我认为您的意思是.persist?您稍后谈到合并,所以也许您的意思是.merge?无论哪种方式,如果您想保留新实体而不是合并,我建议您坚持使用 .persist 。虽然合并并不违法,但它通常用于合并分离的实体等。

既然如此,让我了解您问题的核心,并为您提供可能对您的订单有所帮助的属性(property)。您没有在文本中说明您的 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 的顺序执行,并设置“操作顺序”。

关于java - 使用复合EmbeddedId(包含另一个EmbeddedId的EmbeddedId)插入JPA实体的顺序,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41150922/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com