我正在使用Spring Data和Hibernate,CascadeType.ALL
和orphanRemoval = true
。
问题是,当将子实体从parentX移动到parentY时,如果parentY在parentX之前持久化,则Hibernate会完全从数据库中删除子实体。之后,孩子仍然存在于内存中的parentY中。如果删除它并且保存了parentY,则会抛出EntityNotFoundException
。
我有一个SSCE证明了这一点,并且可以在必要时发布,但这似乎是一个简单的问题。
家长实体:
@Entity
public class TestParent implements Serializable {
private static final long serialVersionUID = 3572015072906463953L;
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "TestParent_GENERATOR")
@SequenceGenerator(name = "TestParent_GENERATOR", initialValue = 1, sequenceName = "TestParent_SEQUENCE", allocationSize = 1)
private long id;
private String name;
@OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.EAGER)
@JoinColumn(name = "TestParent_Id")
private Set<TestChild> testChildren = new HashSet<>();
@SuppressWarnings("unused")
private TestParent() {
}
public TestParent(String name) {
this.name = name;
}
public String getName() {
return this.name;
}
public void addChild(TestChild child) {
this.testChildren.add(child);
}
public void removeChild(TestChild child) {
this.testChildren.remove(child);
}
public TestChild findChild(String childsName) {
for (TestChild testChild : this.testChildren) {
if (testChild.getName().equals(childsName)) {
return testChild;
}
}
return null;
}
}
儿童实体:
@Entity
public class TestChild implements Serializable {
private static final long serialVersionUID = -1594688339088954284L;
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "TestChild_GENERATOR")
@SequenceGenerator(name = "TestChild_GENERATOR", initialValue = 1, sequenceName = "TestChild_SEQUENCE", allocationSize = 1)
private long id;
private String name;
@SuppressWarnings("unused")
private TestChild() {
}
public TestChild(String name) {
this.name = name;
}
public String getName() {
return this.name;
}
}
答案 0 :(得分:2)
从Hibernate的角度来看,实体映射是不完整的,这就是为什么你可能会得到意想不到的结果。最大的罪魁祸首是orphanRemoval = true
,它在不使用mappedBy = ...
的情况下使用。虽然JPA规范不需要用mappedBy = ...
指定orphanRemoval = true
,但是Hibernate无法确定一对多关联的many
侧的实体是否真的孤立{if} { <1}}未指定。
以下映射将纠正行为:
mappedBy = ...
请注意,class TestParent {
@OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.EAGER, mappedBy = "testParent")
private Set<TestChild> testChildren = new HashSet<TestChild>();
}
class TestChild {
@JoinColumn(name = "TestParent_Id")
@ManyToOne
private TestParent testParent;
}
需要移至@JoinColumn(name = "TestParent_Id")
方。
您还需要非常小心父母的变化。如果孩子留在前一个父母的@ManyToOne
集合中,则更改将不会生效。
我创建了一个sample project来演示正常工作的JPA配置。该项目包含一个模拟以下情况的单元测试:
children
实例Child
。c
实例Parent
。a
实例Child
已添加/分配到c
实例Parent
。a
已保存。这级联到a
,也会被保存。c
实例Parent
。b
实例Child
已添加/分配到c
实例Parent
。b
已保存。这级联到b
,也会被保存。在这种情况下,我们希望执行以下SQL查询:
c
如果以INSERT INTO parent (...) VALUES (...);
INSERT INTO child (...) VALUES (...);
INSERT INTO parent (...) VALUES (...);
UPDATE child SET ...;
运行单元测试,您将看到正在按预期执行的SQL查询。