2011-12-22 69 views
0

更新:我重写了这个问题以尽可能多地理解并提供最多的信息。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对象被删除。这不好。

此外,我想确保当我尝试持续它时,hibernate不会将Foo看作重复行,因为FooLnkBar是另一个表中的值。

如果我从数据库中删除Foo,FooLnkBar也应该从数据库中删除。

如果我从数据库中删除FooLnkBar,Foo不应该从数据库中删除。

我正在寻找一种方法来获取所有案例的工作。

更新:更改此修复案例2,但打破案例1:

@ManyToOne(cascade = CascadeType.PERSIST, fetch = FetchType.LAZY) 
    @JoinColumn(name = "SomeID", nullable = false) 
    private Foo foo; 

在案例1中的错误消息现在是“非空属性引用null或瞬时值:FooLnkBar.foo”

+0

'addSet'方法到底做了什么? – 2011-12-22 17:58:30

+0

在这个例子中,这是一个虚拟的方法,将一组ObjectTwos添加到ObjectOne – user973479 2011-12-22 18:16:25

+0

你能告诉我们你的xml映射代码吗?或注释配置? – 2011-12-22 18:25:56

回答

2

它似乎在做你刚刚告诉它的内容:

ObjectOne obj1 = findInDatabase();

加载持久对象。

obj1.addSet(objTwoSet);

将集合的内容添加到对象的现有集合(从而“合并”这两个集合)。

sessionFactory.getCurrentSession()。saveOrUpdate(obj1);

不必要地saveOrUpdate()该对象。该对象已经连接到会话,因此当封闭事务提交时,它的状态将被正确保持。你可以放弃这条线。

为了清除设定的现有内容,并添加所有新内容,你应该做的正是:

obj1.clearSet(); 
obj1.addSet(objTwoSet); 

更新: A设置不被视为一个单一的,离散的对象休眠。它只是一组实体,所以当你说“从数据库中删除第一个集合”时,实际上是说“从数据库中删除第一个集合中包含的所有实体”或“分离所有包含在来自数据库中给定对象的第一组“。

对于前者,您可以使用orphanRemoval attribute of @OneToMany,并参阅参考指南所示的prototypical parent/child relationships

对于后者,你只是做你正在做的事情,但如果它是双向关系,你还必须正确设置另一端。因此,而不是一个简单的:

obj1.addSet(objTwoSet); 

你需要的东西,如:

for (ObjectTwo o : obj1.getSet()) { 
    o.setObjectOne(null); 
} 
obj1.addSet(objTwoSet); 

这很可能是你的问题。将对象模型保持在断开状态是不妥的。

+0

我重写了我的问题,使其更清晰 – user973479 2011-12-22 18:20:29

+0

另外,如果我必须调用clearSet方法,然后调用另一个方法来添加一个新集合I将不得不撤回现有的设置并手动比较它们,看看是否有差异。换句话说,我不想清除然后重新添加相同的集合。我会假设冬眠有一些办法自动为你做这一切? – user973479 2011-12-22 18:23:21

+1

更新了我的回答 – 2011-12-22 18:38:43

1

尝试以下操作执行:

public class ObjectOne{ 
    @OneToMany(cascade = CascadeType.ALL, orphanRemoval=true) 
    Set<ObjectTwo> obj2; 

    public void addSet(Set<ObjectTwo> newSet){ 
    obj2.clear(); 
    obj2.addAll(newSet); 
    } 
} 

是联想双向的,即不ObjectTwo还引用ObjectOne?如果是这样,在清除obj2集之前,还必须将此引用设置为null。

public class ObjectOne{ 
    @OneToMany(cascade = CascadeType.ALL, orphanRemoval=true) 
    Set<ObjectTwo> obj2; 

    public void addSet(Set<ObjectTwo> newSet){ 
    for (ObjectTwo two : obj2) { 
     two.setObjectOne(null); 
    } 
    obj2.clear(); 
    for (ObjectTwo two : newSet) { 
     two.setObjectOne(this); 
     obj2.add(two); 
    } 
    } 
} 
+0

Tscho,我很抱歉,但我认为我原来的问题写得不好。请参阅最新版本,我的情况可能更有意义。 – user973479 2011-12-23 13:57:44