要保存或者更新一个对象关联图中所有的所有对象,你必须做到:
保证每一个对象都执行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()。
对一种关联(多对一,或者集合)使用cascade="all"映射,就把这种关联标记为一种父/子(parent/child)风格的关系,对父对象进行保存/更新/删除会导致对(所有)子对象的保存/更新/删除。但是这个比喻并不是特别确切。如果父对象解除了对某个子对象的关联,那这个子对象就不会被自动删除了。除非这是一个一对多的关联,并且标明了cascade="all-delete-orphan"(所有-删除-孤儿)。级联操作的精确语义在下面列出:
如果父对象被保存,所有的子对象会被传递到saveOrUpdate()方法去执行
如果父对象被传递到update()或者saveOrUpdate(),所有的子对象会被传递到saveOrUpdate()方法去执行
如果一个临时的子对象被一个持久化的父对象引用了,它会被传递到saveOrUpdate()去执行
如果父对象被删除了,所有的子对象对被传递到delete()方法执行
如果临时的子对象不再被持久化的父对象引用,什么都不会发生(必要时,程序应该明确的删除这个子对象),除非声明了cascade="all-delete-orphan",在这种情况下,成为“孤儿”的子对象会被删除。
Hibernate还没有完全实现“通过可触及性决定持久化”,后者暗示会对垃圾收集进行(效率不高的)持久化。但是,因为很广泛的呼声,Hibernate实现了一种意见,如果一个实体被一个持久化的对象引用,它也会被持久化。注明了cascade="save-update"的关联就是按照这种思路运作的。如果你希望在你的整个程序中都贯彻这个方法,你可以在<hibernate-mapping>元素的default-cascade属性中指定这种级联方式。
每件隔一段时间,Session会执行一些必需的SQL语句来把内存中的对象和JDBC连接中的状态进行同步。这个过程被称为清洗(flush),默认会在下面的时间点执行:
在某些find()或者iterate()调用的时候
在net.sf.hibernate.Transaction.commit()的时候
在Session.flush()的时候
涉及的SQL语句会按照下面的顺序安排:
所有对实体进行插入的语句,其顺序按照对象执行Session.save()的时间顺序
所有对实体进行更新的语句
所有进行集合删除的语句
所有对集合元素进行删除,更新或者插入的语句
所有进行集合插入的语句
所有对实体进行删除的语句,其顺序按照对象执行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
结束一个session包括四个独立的步骤:
清洗session
提交事务
关闭session
处理异常
如果你正在使用TransactionAPI,你就不用担心这个步骤。在事务提交的时候,隐含就会包括这一步。否则,你应该调用Session.flush()来确保你所有的修改都与数据库同步。
如果你正在使用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内部状态的完整性。
调用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();
}
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() );
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] ); } }