在使用NHibernate的时候。在Session中会有3种状态。
1. 瞬时状态 (Transient)
由 new 命令开辟内存空间的对象,也就是平时所熟悉的普通对象。
如: Student stu = new Student(); 瞬时对象特点:(1) 不和 Session 实例关联, 也就是说。在Session中没有缓存指向这个对象地址的引用; (2) 在数据库中没有和瞬时对象关联的记录。也就是说。这个对象的主键属性,在数据库中没有哪一行的主键与其相应。
2. 持久状态 (Persistent)
持久对象总是与 Session 和 Transaction 相关联,在一个 Session 中,对持久对象的改变不会立即对数据库进行变更,而必须在Transaction 终止,也就是执行 commit() 之后,才在数据库中真正执行 SQL 进行变更。持久对象的状态才会与数据库进行同步。在同步之前的持久对象称为脏 (dirty) 对象。瞬时对象转为持久对象:(1) 通过 Session 的 save() 和 saveOrUpdate() 方法把一个瞬时对象与数据库相关联。这个瞬时对象就成为持久化对象。
(2) 使用 fine(),get(),load() 和 iterater() 方法查询到的数据对象。将成为持久化对象。持久化对象的特点:(1) 和 Session 实例关联,而且在Session中有指向这个内存对象地址的引用 (2) 在数据库中有和持久对象关联的记录。
3. 脱管状态 (Detached)
与持久对象关联的 Session 被关闭后,对象就变为脱管对象。对脱管对象的引用依旧有效,对象可继续被改动。
脱管对象特点:(1) 本质上和瞬时对象同样。 仅仅是比瞬时对象多了一个数据库记录标识值 id. 注意这是数据库自增主键的情况。假设是assgin id情况。那么与瞬时对象可能就全然一样了。(2) 在数据库中有与其相应的记录。持久对象转为脱管对象:当运行 close() 或 clear(),evict() 之后,持久对象会变为脱管对象。
瞬时对象转为持久对象:通过 Session 的 update(),saveOrUpdate() 和 lock() 等方法,把脱管对象变为持久对象。
终于总结起来,就是要分析session缓存中的id,referrence键值对是否志向指定对象的问题。transient不指向,persistent指向,detached不指向,但与ransient不同的是,数据库中已经有了对象相应的记录。
关于Session一些方法的执行原理。
Session.save(user):1, 把User对象增加Session缓存中,使它变成持久化对象。此时并没有数据库中相应的记录,清理缓存后,把SQL语句同步到数据库。2. 选用映射文件指定的标识生成ID;3。在Session清理缓存时候运行:在底层生成一个insert sql语句,把对象存入数据库。
注意:在你运行Session.save(user)后。在Session清理缓存前,假设你改动user对象属性值。那么终于存入数据库的值将是最后改动的值;此过程中ID不能被改动;
Session.delete(user):假设user是持久化对象,则运行删除操作,相同底层数据库的运行条件是:在Session清理缓存时候;假设user是游离对象,将user对象和Session关联,使之成为持久化对象,所以这时候会发出select的SQL语句,这就是为什么你用NHibernate删除一个对象的时候。它会先发出select的语句。然后依照user 是持久化对象的过程运行。
Session.update(user):假设user是持久化对象。那么不进行不论什么操作,一切操作都是等到清空缓存的时候同步到数据库。假设user是游离对象。那么也是先转为持久对象。然后再等清理缓存的时候同步到数据库。所以Update的时候也会发出select语句。
Session.SaveOrUpdate(user):假设如今session中没有user的缓存,说明这是一个transient或者detached的对象,它会先向数据库发出select语句,注意select语句发出之前是会Flush缓存的,防止select到的是未更新的数据库内容。
发出select语句后,假设没有查询到不论什么内容,说明这是一个新的对象,transient的。这时会把这个transient的对象放入到session缓存中,成为persistent状态。这是在session中标记一下这个对象相应的是什么样的SQL操作,在终于Flush拼接SQL语句的时候,是insert还是update. 假设查询到了记录,说明这是一个detached的对象。这时会把这个对象放入到session缓存中,变成persistent状态。
最后,再调用session.Flush()的时候,会生成一些SQL语句,把这些内容同步到数据库中。假设如今session中有这个user对象所在地址的缓存,那么什么也不用做。Flush的时候同步到数据库就可以。
一种特别情况,假设session中没有这个对象的缓存,可是却有其它的某个对象缓存,有同一个id值存在,这是就会抛出异常。
Session 什么时候清理缓存,这是一个非常关键的问题。什么是缓存清理?就是缓存中主键key相应的内存中的对象与数据库中同主键的记录进行同步的过程,也就是与数据库发生了交互,全部内存中对象的变动都提交到了数据库,注意,并不一定永久性的保持到数据库,这要看是否transaction的提交。而且。session缓存中的id与对象引用也清除。
清理缓存的时机例如以下,1,commit() 方法被调用时 2,查询时会主动commit清理缓存。保证查询结果能反映对象的最新状态。3,显示的调用session 的 flush方法。
注意Session的commit()和flush()方法的差别,flush()方法进行清理缓存的操作,运行一系列的SQL语句,假设没有事务,则持久化到数据库。
假设是事务,尽管在数据库中运行了SQL,但不会提交事务。是能够回滚的。commit()方法先调用flush()。然后提交事务。
提交事务意味着对数据库所做的更新被永久保存下来。一般使用方法是Ssession.save(user); Session.flush(); 当你调用save方法的时候,这个实体对象未必已经保存到数据库了,当你调用flush方法的时候是强制将对象保存到数据库。注意。强制保存到数据库的记录假设是在事务中的,也是能够回滚的。
比方说,Flush之后,你去数据库select。会发现这条记录已经存在。可是由于某些原因造成事务回滚了。代码运行完后你再去select。会发现那条记录已经不再了,由于已经回滚到插入之前状态了。