ITEEDU

8.4. 更改在当前session中保存或者装载的对象

持久化实例(就是通过session装载、保存、创建或者查询出的对象)可以被程序操作,所做的任何修改都会在Session清洗(flushed)的时候被持久化(参见后面的“flushing”部分)。所以最直接的更改一个对象的方法就是load()它,然后直接修改即可。

DomesticCat cat = (DomesticCat) sess.load( Cat.class, new Long(69) );
cat.setName("PK");
sess.flush();  // changes to cat are automatically detected and persisted

有些时候这种编程模式显得效率不高,因为它需要在同一个session中先使用SQL SELECT(来装载对象),又有一个SQL UPDATE(来把修改的状态写回)。因此,Hibernate提供了另一种方式。

8.5. 更改在以前session中保存或者装载的对象

很多程序需要在一个事务中获取对象,然后发送到界面层去操作,用一个新的事务来保存修改。(在高同步访问的环境中使用这种方式,经常使用附带版本的数据来保证事务独立性。)这种方法需要和上一节所描述的略微不同的编程模型。Hibernate支持这种模型,因为它提供了Session.update()方法。

// in the first session
Cat cat = (Cat) firstSession.load(Cat.class, catId);
Cat potentialMate = new Cat();
firstSession.save(potentialMate);

// in a higher tier of the application
cat.setMate(potentialMate);

// later, in a new session
secondSession.update(cat);  // update cat
secondSession.update(mate); // update mate

如果拥有catId标识符的Cat在试图update它之前已经被secondSession装载了,会抛出一个异常。

对于给定的临时实例,当且仅当 它们触及的其他临时实例需要保存的时候, 应用程序应该对它们分别各自使用update()。(自动管理生命周期的对象(lifecycle object)除外。)

Hibernate用户曾经要求有一个通用的方法,可以为新建的临时实例生成标识符并保存,或者保存已经存在标识符的临时实例的改动。saveOrUpdate()方法就是用来提供这个功能的。

Hibernate通过对象的标识符的值(或version,或timestamp时间戳)来分辨这是一个“新”(未保存过的)实例,还是一个“已存在”(已经保存或者从先前的session中装载的)的实例。id映射中的unsaved-value(或<version>,或 <timestamp>)用来指定哪个值被用于表示“新”实例。

<id name="id" type="long" column="uid" unsaved-value="null">
    <generator class="hilo"/>
</id>

unsaved-value允许的取值包括:

  • any - always save 永远保存

  • none - always update 永远更新

  • null - 当标识符是空的时候保存(默认情况)

  • valid identifier value (合法的标识符值)- 当标识符是null或者这个给定的值时保存

  • undefined - 对于version 或 timestamp来说的默认值。此时使用标识符检查。(原文: the default for version or timestamp, then identifier check is used.参见下文有进一步描述.)

 

// in the first session
Cat cat = (Cat) firstSession.load(Cat.class, catID);

// in a higher tier of the application
Cat mate = new Cat();
cat.setMate(mate);

// later, in a new session
secondSession.saveOrUpdate(cat);   // update existing state (cat has a non-null id)
secondSession.saveOrUpdate(mate);  // save the new instance (mate has a null id)

saveOrUpdate()的用法和语义看来对初学者来说容易造成困惑。首先,如果你还没有试图在另一个新session中使用来自原session的实例,你根本就不需要使用update()或者saveOrUpdate()方法。有一些程序完全不需要使用这些方法。

通常,update()或saveorUpdate()方法在下列情形下使用:

  • 程序在前面的session中装载了对象

  • 对象被传递到UI(界面)层

  • 对该对象进行了一些修改

  • 对象被传递回业务层

  • 应用程序在第二个session中调用update()保存修改

saveOrUpdate()完成了如下工作:

  • 如果对象已经在这个session中持久化过了,什么都不用做

  • 如果对象没有标识值,调用save()来保存它

  • 如果对象的标识值与unsaved-value中的条件匹配,调用save()来保存它

  • 如果对象使用了版本(version或timestamp),那么除非设置unsaved-value="undefined",版本检查会发生在标识符检查之前.

  • 如果这个session中有另外一个对象具有同样的标识符,抛出一个异常

8.6. 把在先前的session中保存或装载的对象重新与新session建立关联(reassociate)

lock()方法是用来让应用程序把一个未修改的对象重新关联到新session的方法。

//just reassociate: 直接重新关联
sess.lock(fritz, LockMode.NONE);
//do a version check, then reassociate:  进行版本检查后关联
sess.lock(izi, LockMode.READ);
//do a version check, using SELECT ... FOR UPDATE, then reassociate: 使用SELECT ... FOR UPDATE进行版本检查后关联
sess.lock(pk, LockMode.UPGRADE);

8.7. 删除持久化对象

使用Session.delete()会把对象的状态从数据库中移除。当然,你的应用程序可能仍然持有一个指向它的引用。所以,最好这样理解:delete()的用途是把一个持久化实例变成临时实例。

sess.delete(cat);

你可以通过传递给delete()一个Hibernate 查询字符串来一次性删除很多对象。

你现在可以用你喜欢的任何顺序删除对象,不用担心外键约束冲突。当然,如果你搞错了顺序,还是有可能引发在外键字段定义的NOT NULL约束冲突。