持久化实例(就是通过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提供了另一种方式。
很多程序需要在一个事务中获取对象,然后发送到界面层去操作,用一个新的事务来保存修改。(在高同步访问的环境中使用这种方式,经常使用附带版本的数据来保证事务独立性。)这种方法需要和上一节所描述的略微不同的编程模型。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中有另外一个对象具有同样的标识符,抛出一个异常
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);
使用Session.delete()会把对象的状态从数据库中移除。当然,你的应用程序可能仍然持有一个指向它的引用。所以,最好这样理解:delete()的用途是把一个持久化实例变成临时实例。
sess.delete(cat);
你可以通过传递给delete()一个Hibernate 查询字符串来一次性删除很多对象。
你现在可以用你喜欢的任何顺序删除对象,不用担心外键约束冲突。当然,如果你搞错了顺序,还是有可能引发在外键字段定义的NOT NULL约束冲突。