hibernate 的有关问题(session中的get,load,merge,lock,update,evict,clear)

hibernate 的问题(session中的get,load,merge,lock,update,evict,clear)
看文章   
hibernate 的问题(session中的get,load,merge,lock,update,evict,clear)2011-11-11 15:581、当数据库不存在对应ID数据时,调用load()方法将会抛出ObjectNotFoundException异常,get()方法将返回null,我比较喜欢使用get()方法。

2、当对象.hbm.xml配置文件<class>元素的lazy属性设置为true时,调用load()方法时则返回持久对象的代理类实例,此时的代理类实例是由运行时动态生成的类,该代理类实例包括原目标对象的所有属性和方法,该代理类实例的属性除了ID不为null外,所在属性为null值,查看日志并没有Hibernate SQL输出,说明没有执行查询操作,当代理类实例通过getXXX()方法获取属性值时,Hiberante才真正执行数据库查询操作。当对象.hbm.xml配置文件<class>元素的lazy属性设置为false时,调用load()方法则是立即执行数据库并直接返回实体类,并不返回代理类。而调用get()方法时不管lazy为何值,都直接返回实体类。

3、load()和get()都会先从Session缓存中查找,如果没有找到对应的对象,则查询Hibernate二级缓存,再找不到该对象,则发送一条SQL语句查询。





lock()方法与update()方法的区别

在执行lock()方法时,会立即使用查询语句查询版本号进行版本检查,并不会执行一个update操作。

而update()方法,并不会进行版本检查,知道session.flush()时候,会先进行版本检查,再进行更新操作。

这两个方法都把一个游离对象与一个session实例关联起来。





lock是把一个没有更改过的脱管状态的对象变成持久状态
1.调用lock把对象从脱管状态变成持久状态
2.更改持久状态的对象的内容
3.等待flush或者手动flush


session.merge ()方法



你是说session.buildLockRequest(LockOptions.NONE/READ/UPGRADE).lock(Object entity);吗?
这句话整体上的作用是将指定的entity对象放入session对象的persistenceContext(简称PC)中去,打上MANAGED标记。
NONE和READ还有UPGRADE是三个LockOptions的选项。NONE表示将entity对象放入PC中的时候不会向数据库发出查询语句;READ选项表示将entity对象放入PC中的时候会同时向数据库发出select查询语句;UPGRADE表示将entity对象放入PC中的时候会向数据库发出select查询语句,而且,还会加上for update语句。
for update语句的作用是在查询的同时将数据锁住,其他人企图改变数据的时候就会进入等待状态,直到加锁的一方完成一次事务操作。我估计你说的锁是指这个吧。




该方法将修改表中记录,其所需要的实体状态为脱管状态,但是注意,它并不影响调用方法前后的状态,也即该实体依然是脱管状,见例6.4。

例6.4:session.merge ()方法对状态的变化

     public void run() {

          //创建UserInfo实例

          UserInfo userInfo = new UserInfo();

          //使之成为脱管状态

          userInfo.setId(11112);

          userInfo.setName("RW3");

          userInfo.setSex("M");

          //创建UserInfo实例

          UserInfo userInfo2 = new UserInfo();

          //使之成为脱管状态

          userInfo2.setId(11112);

          userInfo2.setName("RW4");

          userInfo2.setSex("F");

          //启动Session

          Session session = HibernateSessionFactory.currentSession();

          //启动事务

          Transaction tx = session.beginTransaction();

          //调用merge方法,此时UserInfo实体状态并没有被持久化

          session.merge(userInfo);

          //调用merge方法,此时UserInfo实体状态并没有被持久化

          //但是数据库中的记录被更新了

          ①session.merge(userInfo2);

          //merge方法与update方法的差别在于针对同样的操作update方法会报错

          //原因在于update方法使得实体状态成为了持久化状态,而Session中不允许两个持久化实体有同样的持久化标识

          ②//session.update(userInfo);

          //session.update(userInfo2);

         //以下两句不会发送SQL,因为userInfo2不是持久化状态的实体

         ③userInfo2.setName("RW5");

          userInfo2.setSex("M");

          //提交事务

          tx.commit();

          //关闭Hibernate Session

          HibernateSessionFactory.closeSession();

     }

针对该段代码将执行如下SQL语句:

Hibernate:

/* ①session.merge(userInfo2)的动作 */

select

        userinfo0_.id as id0_0_,

        userinfo0_.NAME as NAME0_0_,

        userinfo0_.SEX as SEX0_0_,

        userinfo0_.roomid as roomid0_0_

    from

        userinfo userinfo0_

    where

        userinfo0_.id=?

Hibernate:

/* ①session.merge(userInfo2)的动作 */

update

            userinfo

        set

            NAME=?,

            SEX=?,

            roomid=?

        where

            id=?

session.merge()方法会首先发送一句select语句,去数据库端获取UserInfo持久化标识所对应的表记录;然后自动生成一个持久化状态的UserInfo实体,与脱管状态的UserInfo实体做比较是否有所改变;一旦发生了改变,才会发送update语句执行更新。而按执行顺序,若两句session.merge()方法针对同一个脱管状态的UserInfo实体,那其结果只会执行最后一个session.merge()方法所发出的update语句。即使执行了session.merge()方法,UserInfo实体依然是脱管状态,因此③userInfo2. setName("RW5")的语句不会同步数据库中的表。





session.evict(obj),会把指定的缓冲对象进行清除
session.clear(),把缓冲区内的全部对象清除,但不包括操作中的对象

所以,hibernate执行的顺序如下,

(1)生成一个事务的对象,并标记当前的Session处于事务状态(注:此时并未启动数据库级事务)。
(2)应用使用s.save保存对象,这个时候Session将这个对象放入entityEntries,用来标记对象已经和当前的会话建立了关联,由于应用对对象做了保存的操作,Session还要在insertions中登记应用的这个插入行为(行为包括:对象引用、对象id、Session、持久化处理类)。
(3)s.evict将对象从s会话中拆离,这时s会从entityEntries中将这个对象移出。
(4)事务提交,需要将所有缓存flush入数据库,Session启动一个事务,并按照insert,update,……,delete的顺序提交所有之前登记的操作(注意:所有insert执行完毕后才会执行update,这里的特殊处理也可能会将你的程序搞得一团糟,如需要控制操作的执行顺序,要善于使用flush),现在对象不在entityEntries中,但在执行insert的行为时只需要访问insertions就足够了,所以此时不会有任何的异常。异常出现在插入后通知Session该对象已经插入完毕这个步骤上,这个步骤中需要将entityEntries中对象的existsInDatabase标志置为true,由于对象并不存在于entityEntries中,此时Hibernate就认为insertions和entityEntries可能因为线程安全的问题产生了不同步(也不知道Hibernate的开发者是否考虑到例子中的处理方式,如果没有的话,这也许算是一个bug吧),于是一个net.sf.hibernate.AssertionFailure就被抛出,程序终止

一般错误的认为s.save会立即的执行,而将对象过早的与Session拆离,造成了Session的insertions和entityEntries中内容的不同步。所以我们在做此类操作时一定要清楚Hibernate什么时候会将数据flush入数据库,在未flush之前不要将已进行操作的对象从Session上拆离。
解决办法是在save之后,添加session.flush。