ITEEDU

8.8. 对象图(Graphs of objects)

要保存或者更新一个对象关联图中所有的所有对象,你必须做到:

  • 保证每一个对象都执行save(), saveOrUpdate() 或 update()方法,或者,

  • 在定义关联对象的映射时,使用cascade="all"或cascade="save-update"。

类似的,要删除一个关系图中的所有对象,必须:

  • 对每一个对象都执行delete(),或者

  • 在定义关联对象的映射时,使用cascade="all",cascade="all-delete-orphan"或cascade="delete"。

建议:

  • 如果子对象的生命期是绑定到父对象的生命期的,通过指定cascade="all"可以把它变成一个自动管理生命周期的对象(lifecycle object)

  • 否则,必须在应用程序代码中明确地执行save()和delete()。如果你想少敲一些代码,可以使用cascade="sve-update",然后只需明确地delete()。

8.8.1. 自动管理生命周期的对象(lifecycle object)

对一种关联(多对一,或者集合)使用cascade="all"映射,就把这种关联标记为一种父/子(parent/child)风格的关系,对父对象进行保存/更新/删除会导致对(所有)子对象的保存/更新/删除。但是这个比喻并不是特别确切。如果父对象解除了对某个子对象的关联,那这个子对象就不会被自动删除了。除非这是一个一对多的关联,并且标明了cascade="all-delete-orphan"(所有-删除-孤儿)。级联操作的精确语义在下面列出:

  • 如果父对象被保存,所有的子对象会被传递到saveOrUpdate()方法去执行

  • 如果父对象被传递到update()或者saveOrUpdate(),所有的子对象会被传递到saveOrUpdate()方法去执行

  • 如果一个临时的子对象被一个持久化的父对象引用了,它会被传递到saveOrUpdate()去执行

  • 如果父对象被删除了,所有的子对象对被传递到delete()方法执行

  • 如果临时的子对象不再被持久化的父对象引用,什么都不会发生(必要时,程序应该明确的删除这个子对象),除非声明了cascade="all-delete-orphan",在这种情况下,成为“孤儿”的子对象会被删除。

8.8.2. 通过可触及性决定持久化(Persistence by Reachability)

Hibernate还没有完全实现“通过可触及性决定持久化”,后者暗示会对垃圾收集进行(效率不高的)持久化。但是,因为很广泛的呼声,Hibernate实现了一种意见,如果一个实体被一个持久化的对象引用,它也会被持久化。注明了cascade="save-update"的关联就是按照这种思路运作的。如果你希望在你的整个程序中都贯彻这个方法,你可以在<hibernate-mapping>元素的default-cascade属性中指定这种级联方式。

8.9. 清洗(Flushing) --

这个词很难翻译,不能使用“刷新”,因为刷新一词已经被"refresh"使用了。有什么好的建议?

每件隔一段时间,Session会执行一些必需的SQL语句来把内存中的对象和JDBC连接中的状态进行同步。这个过程被称为清洗(flush),默认会在下面的时间点执行:

  • 在某些find()或者iterate()调用的时候

  • 在net.sf.hibernate.Transaction.commit()的时候

  • 在Session.flush()的时候

涉及的SQL语句会按照下面的顺序安排:

  1. 所有对实体进行插入的语句,其顺序按照对象执行Session.save()的时间顺序

  2. 所有对实体进行更新的语句

  3. 所有进行集合删除的语句

  4. 所有对集合元素进行删除,更新或者插入的语句

  5. 所有进行集合插入的语句

  6. 所有对实体进行删除的语句,其顺序按照对象执行Session.delete()的时间顺序

(有一个例外时,如果对象使用native方式进行 ID 生成的话,它们一执行save就会被插入。)

除非你明确地发出了flush()指令,关于Session合时会执行这些JDBC调用是完全无法保证的,只能保证它们执行的前后顺序。当然,Hibernate保证,Session.find(..)绝对不会返回已经失效的数据,也不会返回错误数据。

也可以改变默认的设置,来让清洗发生的不那么频繁。FlushMode类定义了三种不同的方式。大部分情况下,它们只由当你在处理“只读”的事务时才会使用,可能会得到一些(不是那么明显的)性能提高。

sess = sf.openSession();
Transaction tx = sess.beginTransaction();
sess.setFlushMode(FlushMode.COMMIT); //allow queries to return stale state
Cat izi = (Cat) sess.load(Cat.class, id);
izi.setName(iznizi);
// execute some queries....
sess.find("from Cat as cat left outer join cat.kittens kitten"); //change to izi is not flushed!!
....
tx.commit(); //flush occurs

8.10. 结束一个Session

结束一个session包括四个独立的步骤:

  • 清洗session

  • 提交事务

  • 关闭session

  • 处理异常

8.10.1. 清洗(Flush)session

如果你正在使用TransactionAPI,你就不用担心这个步骤。在事务提交的时候,隐含就会包括这一步。否则,你应该调用Session.flush()来确保你所有的修改都与数据库同步。

8.10.2. 提交事务

如果你正在使用Hibernate 的Transaction API,代码类似这样:

tx.commit(); // flush the Session and commit the transaction

如果你自行管理JDBC事务,你应该手工对JDBC 连接执行commit()。

sess.flush();
sess.connection().commit();  // not necessary for JTA datasource

如果你决定提交你的更改:

tx.rollback();  // rollback the transaction

或者:

// not necessary for JTA datasource, important otherwise
sess.connection().rollback();

如果你回滚了事务,你应该立即关闭和取消当前session,确保Hibernate内部状态的完整性。

8.10.3. 关闭session

调用Session.close()就标志这个session进入了尾声。close()主要的含义就是与这个session相关的JDBC连接会被放弃。

tx.commit();
sess.close();
sess.flush();
sess.connection().commit();  // not necessary for JTA datasource
sess.close();
Session sess = factory.openSession();
Transaction tx = null;
try {
    tx = sess.beginTransaction();
    // do some work
    ...
    tx.commit();
}
catch (Exception e) {
    if (tx!=null) tx.rollback();
    throw e;
}
finally {
    sess.close();
}

如果你是手工管理JDBC事务的,用下面这段:

Session sess = factory.openSession();
try {
    // do some work
    ...
    sess.flush();
    sess.connection().commit();
}
catch (Exception e) {
    sess.connection().rollback();
    throw e;
}
finally {
    sess.close();
}

如果你是从JTA中获得数据源的:

UserTransaction ut = .... ;
Session sess = factory.openSession();
try {
    // do some work
    ...
    sess.flush();
}
catch (Exception e) {
    ut.setRollbackOnly();
    throw e;
}
finally {
    sess.close();
}

8.11. 拦截器(Interceptors)

Interceptor接口提供从session到你的应用程序的回调方法,让你的程序可以观察和在持久化对象保存/更改/删除或者装载的时候操作它的属性。一种可能的用途是用来监视统计信息。比如,下面的Interceptor会自动在一个Auditable创建的时候设置其createTimestamp,并且当它被更改的时候,设置其lastUpdateTimestamp属性。

package net.sf.hibernate.test;

import java.io.Serializable;
import java.util.Date;
import java.util.Iterator;

import net.sf.hibernate.Interceptor;
import net.sf.hibernate.type.Type;

public class AuditInterceptor implements Interceptor, Serializable {

    private int updates;
    private int creates;

    public void onDelete(Object entity,
                         Serializable id,
                         Object[] state,
                         String[] propertyNames,
                         Type[] types) {
        // do nothing
    }

    public boolean onFlushDirty(Object entity, 
                                Serializable id, 
                                Object[] currentState,
                                Object[] previousState,
                                String[] propertyNames,
                                Type[] types) {

        if ( entity instanceof Auditable ) {
            updates++;
            for ( int i=0; i < propertyNames.length; i++ ) {
                if ( "lastUpdateTimestamp".equals( propertyNames[i] ) ) {
                    currentState[i] = new Date();
                    return true;
                }
            }
        }
        return false;
    }

    public boolean onLoad(Object entity, 
                          Serializable id,
                          Object[] state,
                          String[] propertyNames,
                          Type[] types) {
        return false;
    }

    public boolean onSave(Object entity,
                          Serializable id,
                          Object[] state,
                          String[] propertyNames,
                          Type[] types) {
        
        if ( entity instanceof Auditable ) {
            creates++;
            for ( int i=0; i<propertyNames.length; i++ ) {
                if ( "createTimestamp".equals( propertyNames[i] ) ) {
                    state[i] = new Date();
                    return true;
                }
            }
        }
        return false;
    }

    public void postFlush(Iterator entities) {
        System.out.println("Creations: " + creates + ", Updates: " + updates);
    }

    public void preFlush(Iterator entities) {
        updates=0;
        creates=0;
    }
    
    ......
    ......
    
}
当session被创建的时候,就应该指定拦截器。
Session session = sf.openSession( new AuditInterceptor() );

8.12. 元数据(Metadata) API

Hibernate对所有的实体和值类型都需要一个非常丰富的元级别(meta-level)模型。有时候,这个模型对应用程序本身也会非常有用。比如说,应用程序可能使用Hibernate的元数据来实现一种“智能”的深度拷贝算法,来理解哪些对象应该被拷贝(比如,可变的值类型),那些不应该(不可变的值类型和可能的被关联的实体)。

Hibernate通过ClassMetadata接口,CollectionMetadata接口和Type对象树,暴露出元数据。可以通过SessionFactory获取metadata接口的实例。

Cat fritz = ......;
Long id = (Long) catMeta.getIdentifier(fritz);
ClassMetadata catMeta = sessionfactory.getClassMetadata(Cat.class);
Object[] propertyValues = catMeta.getPropertyValues(fritz);
String[] propertyNames = catMeta.getPropertyNames();
Type[] propertyTypes = catMeta.getPropertyTypes();
// get a Map of all properties which are not collections or associations
// TODO: what about components?
Map namedValues = new HashMap();
for ( int i=0; i<propertyNames.length; i++ ) {
    if ( !propertyTypes[i].isEntityType() && !propertyTypes[i].isCollectionType() ) {
        namedValues.put( propertyNames[i], propertyValues[i] );
    }
}