假设我们从Session中装入了一个Parent对象,用户界面对其进行了修改,然后我们希望在一个新的Session里面调用update()来更新它。对象Parent包含了子对象的集合,由于打开了级联更新,Hibernate需要知道哪些子对象是新的,哪些是数据库中已经存在的。我们假设Parent和Child对象的标识属性的类型为java.lang.Long。Hibernate会使用标识属性的值来判断哪些子对象是新的。(你也可以使用version 或 timestamp 属性,参见Section 8.5, “更改在以前session中保存或者装载的对象”.)
unsaved-value属性是用来表示新实例的标识属性值的,缺省为"null",用在Long类型的标识类型再好不过了。如果我们使用原始类型作为标识类型的话,我们在配置Child类映射的时候就必须写:
<id name="id" type="long" unsaved-value="0">
(为版本和时间戳属性进行映射,也会有另一个叫做unsaved-value的属性。)
下面的代码会更新parent和child对象,并且插入newChild对象。
//parent and child were both loaded in a previous session parent.addChild(child); Child newChild = new Child(); parent.addChild(newChild); session.update(parent); session.flush();
好的,对于自动生成标识的情况这样做很方便,但是自分配的标识和复合标识怎么办呢?这是有点麻烦,因为unsaved-value无法区分新对象(标识是用户指定的)和前一个Session装入的对象。在这种情况下,你可能需要给Hibernate一些提示,在调用update(parent)之前:
在这个类的<version> or <timestamp>属性映射上定义unsaved-value="null"或者unsaved-value="negative"。
在对父对象执行update(parent)之前,设定unsaved-value="none"并且显式的调用save()在数据库创建新子对象
在对父对象执行update(parent)之前,设定unsaved-value="any"并且显式的调用update()更新已经装入的子对象
none是自分配标识和复合标识的unsaved-value的缺省值。
There is one further possibility. There is a new Interceptor method named isUnsaved() which lets the application implement its own strategy for distinguishing newly instantiated objects. For example, you could define a base class for your persistent classes.
还有一种可能情况,有一个名为isUnsaved()的拦截器(Interceptor)方法,它允许应用程序自己实现新实例的判断。比如,你可以自己定义一个持久类的祖先类:
public class Persistent implements Lifecycle { private boolean _saved = false; public boolean onSave(Session s) { _saved=true; return NO_VETO; } public void onLoad(Session s, Serializable id) { _saved=true; } ...... public boolean isSaved() { return _saved; } }
And implement isUnsaved()
(saved属性是不会被持久化的。) 现在在onLoad()和onSave()外,还要实现isUnsaved()。
public Boolean isUnsaved(Object entity) { if (entity instanceof Persistent) { return new Boolean( !( (Persistent) entity ).isSaved() ); } else { return null; } } public boolean onLoad(Object entity, Serializable id, Object[] state, String[] propertyNames, Type[] types) { if (entity instanceof Persistent) ( (Persistent) entity ).onLoad(); return false; } public boolean onSave(Object entity, Serializable id, Object[] state, String[] propertyNames, Type[] types) { if (entity instanceof Persistent) ( (Persistent) entity ).onSave(); return false; }
这个问题往往让新手感到迷惑,它确实不太容易消化。不过,经过一些实践以后,你会感觉越来越顺手。父子对象模式已经被广泛的应用在Hibernate应用程序中。
在第一段中我们曾经提到另一个方案。复合元素的语义与父子关系是等同的,但是我们并没有详细讨论。很不幸复合元素还有两个重大限制:复合元素不能拥有collections,并且,除了用于惟一的父对象外,它们不能再作为其它任何实体的子对象。(但是,通过使用<idbag>映射,它们可能拥有代理主键。)