Spring / Hibernate 替换一组级联对象
更新:我重写了这个问题,以使其尽可能有意义并提供最多的信息。
我有以下情况:
@Entity
public class FooLnkBar implements java.io.Serializable {
@ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
@JoinColumn(name = "SomeID", nullable = false)
private Foo foo;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "OtherID", nullable = false)
private Bar bar;
}
@Entity
public class Foo implements java.io.Serializable {
@OneToMany(orphanRemoval=true, cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "foo")
private Set<FooLnkBar> fooLnkBars = new HashSet<FooLnkBar>(0);
}
案例 1:通过保留 FooLnkBar 保存所有内容
// This correctly saves everything into the database in one shot
Foo foo = new Foo();
Bar bar = new Bar();
FooLnkBar flb = new FooLnkBar();
flb.setFoo(foo);
flb.setBar(bar);
persist(flb);
案例 2:修改 FooLnkBar 并保留 Foo
// Load a Foo from the database and get the set of FooLnkBars
Foo foo = loadFooFromDatabase();
Set<FooLnkBar> fooLnkBars = foo.getFooLnkBars();
// Delete all existing FooLnkBar objects. Alternatively use removeAll()
Iterator<FooLnkBar> it = fooLnkBars.iterator();
while (it.hasNext()) {
FooLnkBar o = it.next();
it.remove();
}
// Add some new FooLnkBars
Foo foo = new Foo();
Bar bar = new Bar();
FooLnkBar flb = new FooLnkBar();
fooLnkBars.add(flb);
foo.setFooLnkBars(fooLnkBars);
// Save all changes
persist(foo);
案例 2 不起作用。由于cascadeType的存在,这也删除了FooLnkBar所在的Foo,因此案例2中的整个Foo对象都被删除了。这很糟糕。
此外,我想确保当我尝试保留 Foo 时,hibernate 不会将其视为重复行,因为 FooLnkBar 是另一个表中的值。
如果我从数据库中删除 Foo,则 FooLnkBar 也应该从数据库中删除。
如果我从数据库中删除 FooLnkBar,则 Foo 不应从数据库中删除。
我正在寻找一种让所有案例都能发挥作用的方法。
更新:更改此修复了情况 2,但破坏了情况 1:
@ManyToOne(cascade = CascadeType.PERSIST, fetch = FetchType.LAZY)
@JoinColumn(name = "SomeID", nullable = false)
private Foo foo;
情况 1 中的错误消息现在是“not-null 属性引用 null 或瞬态值:FooLnkBar.foo”
Update: I rewrote the question to make as much sense as possible and provide the most information.
I have the following:
@Entity
public class FooLnkBar implements java.io.Serializable {
@ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
@JoinColumn(name = "SomeID", nullable = false)
private Foo foo;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "OtherID", nullable = false)
private Bar bar;
}
@Entity
public class Foo implements java.io.Serializable {
@OneToMany(orphanRemoval=true, cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "foo")
private Set<FooLnkBar> fooLnkBars = new HashSet<FooLnkBar>(0);
}
Case 1: Save everything by persisting FooLnkBar
// This correctly saves everything into the database in one shot
Foo foo = new Foo();
Bar bar = new Bar();
FooLnkBar flb = new FooLnkBar();
flb.setFoo(foo);
flb.setBar(bar);
persist(flb);
Case 2: Modify a FooLnkBar and persist a Foo
// Load a Foo from the database and get the set of FooLnkBars
Foo foo = loadFooFromDatabase();
Set<FooLnkBar> fooLnkBars = foo.getFooLnkBars();
// Delete all existing FooLnkBar objects. Alternatively use removeAll()
Iterator<FooLnkBar> it = fooLnkBars.iterator();
while (it.hasNext()) {
FooLnkBar o = it.next();
it.remove();
}
// Add some new FooLnkBars
Foo foo = new Foo();
Bar bar = new Bar();
FooLnkBar flb = new FooLnkBar();
fooLnkBars.add(flb);
foo.setFooLnkBars(fooLnkBars);
// Save all changes
persist(foo);
Case 2 does not work. Because of the cascadeType, this also removes the Foo in which the FooLnkBar resides, so the entire Foo object in Case 2 is deleted. This is bad.
Furthermore, I want to ensure that hibernate doesn't see Foo as a duplicate row when I try to persist it since FooLnkBar is a value in another table.
If I delete a Foo from the database the FooLnkBar should also be deleted from the database.
If I delete a FooLnkBar from the database the Foo should NOT be deleted from the database.
I am looking for a way to get all cases working.
Update: Changing this fixes Case 2 but breaks Case 1:
@ManyToOne(cascade = CascadeType.PERSIST, fetch = FetchType.LAZY)
@JoinColumn(name = "SomeID", nullable = false)
private Foo foo;
The Error message in Case 1 is now "not-null property references a null or transient value: FooLnkBar.foo"
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
它似乎完全按照你告诉它的那样做:
加载持久对象。
将集合的内容添加到对象的现有集合(从而“合并”两个集合)。
不必要地 saveOrUpdate() 对象。该对象已附加到会话,因此当封闭事务提交时,其状态将正确保留。你可以去掉这条线。
为了清除集合的现有内容并添加所有新内容,您应该执行以下操作:
更新:在 Hibernate 中,集合不被视为单个离散对象。它只是一组实体,因此当您说“从数据库中删除第一个集合”时,您实际上是在说“从数据库中删除第一个集合中包含的所有实体”或“取消关联中包含的所有实体”数据库中给定对象的第一组”。
对于前者,您可以使用 @OneToMany 的 orphanRemoval 属性,参见原型父/子关系参考指南。
对于后者,您只需准确地执行您正在执行的操作即可,但如果是双向关系,则还必须正确设置另一端。因此,您需要的不是简单的
:
这很可能是您的问题。让对象模型处于损坏状态是绝对不行的。
It seems to be doing exactly what you told it to:
Load a persistent object.
Add the content of a set to the object's existing set (thus "merging" the two sets).
Unnecessarily saveOrUpdate() the object. The object is already attached to the session, and therefore its state will properly be persisted when the enclosing transaction commits. You can do away with this line.
In order to clear the existing content of the set and add all new content, you should do exactly that:
Update: A Set isn't treated as a single, discrete object in Hibernate. It's just a grouping of entities, so when you say "remove the first Set from the database", you're actually saying one of "remove all the entities contained in the first set from the database" or "disassociate all the entities contained in the first set from the given object in the database".
For the former, you'd use the orphanRemoval attribute of @OneToMany, and see the prototypical parent/child relationships demonstrated by the reference guide.
For the latter, you just do exactly what you're doing, but if it's a bi-directional relationship, you must also correctly set the other end. So instead of a simple:
you need something like:
This is most likely your problem. It's never okay to leave your object model in a broken state.
尝试以下实现:
关联是否是双向的,即 ObjectTwo 是否也引用 ObjectOne?如果是这样,您还必须在清除 obj2 集之前将此引用设置为 null。
Try the following implementation:
Is the association bi-directional, i.e. does ObjectTwo also reference ObjectOne? If so, you must also set this reference to null before you clear the obj2 set.