ITEEDU

9.4. 级联更新(Using cascading update())

假设我们从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;
}

9.5. 结论

这个问题往往让新手感到迷惑,它确实不太容易消化。不过,经过一些实践以后,你会感觉越来越顺手。父子对象模式已经被广泛的应用在Hibernate应用程序中。

在第一段中我们曾经提到另一个方案。复合元素的语义与父子关系是等同的,但是我们并没有详细讨论。很不幸复合元素还有两个重大限制:复合元素不能拥有collections,并且,除了用于惟一的父对象外,它们不能再作为其它任何实体的子对象。(但是,通过使用<idbag>映射,它们可能拥有代理主键。)