我正在使用@OneToMany注释来保存父实体和子实体,但在特定情况下保存子实体时遇到问题。
子实体在两种情况下被保存:
但是当父项与子项 1 一起插入时,然后在更新父项期间我尝试插入子项 2,然后我无法保存子项 2
它失败,出现以下异常:
o.h.e.jdbc.spi.SqlExceptionHelper - ORA-01407: cannot update ("app_Name"."Child_entity"."REFERENCE_ID") to NULL\n
23:22:06.068 ERROR o.h.i.ExceptionMapperStandardImpl - HHH000346: Error during managed flush [org.hibernate.exception.ConstraintViolationException: could not execute statement]
请参阅我的代码,如下所示:
@Data
@Entity
@Table("Parent_table")
public class Parent_entity implements Serializable {
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER, orphanRemoval =true)
@JoinColumn(name="REFERENCE_ID")
private Set<Child_Entity> childrens ;
}
@Data
@Entity
@Table("child_table")
public class Child_entity implements Serializable {
@Id
@GeneratedValue(generator = "seq_gen", strategy = GenerationType.SEQUENCE)
@SequenceGenerator(name = "seq_gen", sequenceName = "child_SEQ",allocationSize=1)
@Column(name ="col_name")
private Integer asSeq;
@Column(name ="REFERENCE_ID")
private String referenceid;
}
在映射器类中,我显式设置父表的主键。
Oracle 数据库端我添加了以下外键约束
ALTER TABLE child_table
ADD CONSTRAINT FK_parent_table
FOREIGN KEY (REFERENCE_ID)
REFERENCES Parent_table(REFERENCE_ID);
我已经浏览了关于 stackoverflow 的类似问题,这表明如果您将现有列用于外键,则该列的现有值不应为 null。但是在我的情况下,列“REFERENCE_ID”已经不可为空。
请让我知道或建议我是否需要添加其他内容以使其工作。
谢谢。
编辑:在更新方案中,Hibernate正在生成以下查询:
update child_table set reference_id=null where reference_id=? and child_seq=?
其中reference_id是父的主键,child_seq是子级的主键
知道为什么Hibernate试图更新父的主键我正在显式设置父主键的值在孩子的实体中
这里其实有三个问题:
TL;TR: GOTO 2
1. 更新方案
Hibernate正在尝试将reference_id
更新为NULL
,因为它希望将子实体与父实体“分离”。这意味着,在更新期间,您将添加两个新子项,而不是保留一个并添加一个。我还没有看到你的相关代码段,但我认为它可能或多或少看起来像这样:
ParentEntity parent = new ParentEntity();
parent.setId("test");
ChildEntity child1 = new ChildEntity();
child1.setReferenceid(parent.getId());
parent.setChildrens(new HashSet<>(Arrays.asList(child1)));
repository.save(parent);
ChildEntity child2 = new ChildEntity();
child2.setReferenceid(parent.getId());
parent.getChildrens().add(child2);
repository.save(parent);
它最终以 ConstraintVirelationException
结束。在第二次保存
调用中,child1
仍然是一个“分离”的实例,它的 id 为 NULL,Hibernate 将两个孩子都视为新实例。因此,首先它会将它们添加到child_table
,然后尝试通过将其 referenceId
设置为 NULL 来删除“旧”(孤立删除稍后发生,并且有点不相关)。它可以很容易地修复:
// ...
parent = repository.save(parent); // <- save(parent) returns updated object
ChildEntity child2 = new ChildEntity();
child2.setReferenceid(parent.getId());
parent.getChildrens().add(child2);
repository.save(parent);
不再有例外,但它不能解决问题。迟早你会从子集中删除一个孩子,它总是会导致异常。
2. 具有不可为空的连接列的单向一对多关系
建模的规范方式如下:
父实体
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER, orphanRemoval = true)
@JoinColumn(name = "REFERENCE_ID", nullable = false)
private Set<ChildEntity> childrens;
子实体
@Column(name = "REFERENCE_ID", insertable = false, updatable = false)
private String referenceid;
它应该可以工作,但Hibernate会生成不必要的“更新”查询:
select parententi0_.parent_id as parent_i1_1_1_, childrens1_.reference_id as referenc3_0_3_, childrens1_.id as id1_0_3_, childrens1_.id as id1_0_0_, childrens1_.name as name2_0_ ...
select nextval ('child_seq')
select nextval ('child_seq')
insert into child_table (name, reference_id, id) values (?, ?, ?)
insert into child_table (name, reference_id, id) values (?, ?, ?)
update child_table set reference_id=? where id=?
update child_table set reference_id=? where id=?
delete from child_table where id=?
一两件没什么大不了的,但有 100 件?发生这种情况是因为父实体是关系的所有者(由于@JoinTable注释)。它对外键一无所知child_table也不知道如何处理它。
2b. 双向一对一关系的“一半”
或者,我们可以尝试通过删除@JoinColumn
并添加 mappedBy
使 ChildEntity 成为关系的所有者。从理论上讲,关系的另一边应该有相应的@ManyToOne,但似乎没有它就可以了。此解决方案是最佳的,但可能无法移植(到不同的 JPA 提供程序或不同的 Hibernate 版本)。
父实体
@OneToMany(mappedBy = "referenceid", cascade = CascadeType.ALL, fetch = FetchType.EAGER, orphanRemoval = true)
private Set<ChildEntity> childrens;
子实体(无变化,与问题中相同)
@Column(name = "REFERENCE_ID")
private String referenceid;
更新时,Hibernate会生成以下查询:
select parententi0_.parent_id as parent_i1_1_1_, childrens1_.reference_id as referenc3_0_3_, childrens1_.id as id1_0_3_, childrens1_.id as id1_0_0_, childrens1_.name as name2_0_ ...
select nextval ('child_seq')
select nextval ('child_seq')
insert into child_table (name, reference_id, id) values (?, ?, ?)
insert into child_table (name, reference_id, id) values (?, ?, ?)
delete from child_table where id=?
3. 龙目岛生成的等号和哈希码
这与你的问题没有直接关系,但我认为你迟早会遇到这个问题。您正在使用@Data注释(我假设它们是龙目岛的,如果不是,请忽略本节)。默认情况下,它们将从所有字段(包括 id)生成 equals 和 hashCode 方法。在父实体中很好,其中 id 是手动设置的。但是在 ChildEntity 中,id(又名 asSeq
)由数据库生成,它会破坏 hashCode()/equals() 合约。它可能会导致非常偷偷摸摸的错误。来自 Hibernate 文档:
这里的问题是生成的标识符的使用、Set 的协定和 equals/hashCode 实现之间的冲突。Set 表示,当对象是 Set 的一部分时,对象的 equals/hashCode 值不应更改。但这正是这里发生的事情,因为 equals/hasCode 基于(生成的)id,该 id 在提交 JPA 事务之前没有设置。
您可能想在此处阅读更多相关信息:
在这种情况下,我希望检索数据,但在返回数据之前,我希望在不保存/持久化的情况下对其进行更改。 这是我的Rest控制器: 以下是服务实现: 最初我在服务上有注释。通过删除它而不在方法中使用它,我首先想到的是会话将使用存储库打开和关闭,如这里的答案所示。 这不起作用,我在评论中看到“会话将持续整个HTTP请求处理过程。”所以,在我添加的服务中 然后在我添加的方法中 在我做任何改变之前。然而,我所做的任
我的实体更新有问题。如你所见,我有三个实体。 包含类中的列表。 包含类中的列表。 从我的SQL语句中可以看到,字段和是唯一的。我的表中只允许该组合的一行。 当我用类的新列表更新父实体()时,我希望Hibernate删除旧列表本身并创建新实体。一个一个地更新孩子是有点困难的。这就是为什么我要直接删除所有相关的孩子。 当我更新并为其提供包含具有唯一名称的和(数据库中已存在的实体)组合的列表时,我会出现
我有一个User类,它有一些附加属性: 现在,如果我调用一些方法来改变其中一个房子(例如改变它的名称属性),如果我调用,我仍然会得到房子的旧值: 我如何确保它总是更新?
我们使用JPA实体和hibernate来实现持久性。我有一个< code >计划实体和一个< code >升级实体。当我创建一个新的升级并保持它时,计划也在不知何故地得到更新。此更新导致< code > OptimisticLockException ,并阻止进一步升级。这是代码框架- 中没有映射升级。当我运行此 我在控制台日志里看到了这个 我不希望发布有关计划的更新,因为没有任何更改。我只是使用
主要内容:JPA实体更新示例JPA允许我们通过更新实体来更改数据库中的记录。 JPA实体更新示例 在这里,我们将演示如何根据主键更新学生的年龄。 完整的项目代码如下所示 - 这个例子包含以下步骤 - 第1步: 在包下创建一个名为的实体类,它包含属性:,和。 文件:StudentEntity.java 的代码如下 - 第2步: 将实体类和其他数据库配置映射到文件中。 文件:persistence.xml 的代码如下 - 在包下
我需要在另一个相关实体更新后对一个实体执行更新。 我有两个实体:和,关系为1:N。两者都有字段。状态取决于所有子状态字段。因此,如果更新了一个,我需要重新计算的新状态并持久化/更新它。 我实现了一个监听器: 监听器在中进行了注释,并且正在正确调用它。但是,在流程完成后,仍然保持旧状态,即使使用正确的新状态调用。
我有两个实体具有@OneTomany(Entity1)&@ManyToOne(Entity2)双向关系。在@OneTomany关系中,我有@Cascading{Cascade.All}。但是当我在事务或方法中将具有@ManyToOne关系的实体的初始为false的boolean属性更改为true,然后又变回false时,它会导致数据库触发更新查询,将实体的boolean设置为false,这似乎是错
我的父母子女单向关系如下: 我创建一个父实例,给它分配一个子列表,并尝试持久化它,它工作得很好。 当我试图通过子实体单独保存时,我看到insert查询试图将null插入子表中的列PARENT_ID,并导致 异常,同时保存独立于父表的子实体。我试图插入的子实体与已经存在的父实体相关。 在将关系定义为单向关系后,是否无法直接保存子实体?