您的位置:首页 > 其它

hibernate对象的状态的理解(第五天)

2015-10-12 21:05 381 查看
昨天没有写,因为我感觉我对这块没有很明白,所以不能违背自己的理解,今天明白一些了,补上它。

hibernate中的对象分为三个状态,分别是瞬时状态,持久状态和离线状态。

瞬时状态:这个状态指的当前对象在session缓存中不存在,不受session管理,缓存中和数据库中没有相应的OID与当前对象对应。

持久状态:这个状态是指当前对象在缓存中有相应的引用指向他,并且有相应的OID与之对应;对于load方法来说在数据库中也存在(就这个地方我一直纠结,后面说)。

离线状态:对象还存在JVM中(session中没有引用,但是你对象本身没指向null),此时的对象不受session管理,本身来说和游离状态很相似,但其实离线对象是因为持久状态时session关闭了或者clear等操作而变成离线的,但是瞬时状态是持久状态delete后的,也就是说这两个之间的区别是一个在数据库中有OID一个没有OID。

网上找的图,贴上意思一下......



下面的代码来说明一下这三个状态:

public static void main(String[] args) {
// TODO Auto-generated method stub
Session session = HibernateUtil.openSession();
Transaction trans = null;
try {
trans = session.beginTransaction();
Employee employee1 = new Employee("feizhu","cc@qq.com",new Date());//这时对象是瞬时状态
session.save(employee1);//这时对象变成持久状态,受session管理。
System.out.println(employee1.getId());
employee1.setName("lua");
trans.commit();

} catch (Exception e) {
// TODO Auto-generated catch block
if(trans!=null)
trans.rollback();
e.printStackTrace();
}finally{
if(session!=null||session.isOpen())
session.close();              //session关闭,对象变成离线状态。
}
}
}


这段代码最后能输出三条SQL 一条是查询当前最大主键(我的主键生成策略是increment,我对主键生成策略还没有完全理解就先不说这个),每次save都会执行所以不用关。然后是一条insert一条update。

在我们new一个对象(或者spring管理对象)的时候,这个对象是瞬时的,为什么说瞬时呢?我们一般初始化完一个对象就会将他持久化,所以我理解的瞬时就是这个状态应该会持续的时间短一点。

当我们执行save方法的时候,我们的对象就收到session的绑定了,但是庆大家一定注意:此时的对象只是在session缓存中有相应的OID对应,但是数据库中绝对没有这个对象(前提这个对象是全新的new出来的,而不是别的状态过去的)。我这么讲是因为我被很多资料和博客上的说法所迷惑,也许是我理解的比较极端,但是他们说的也是肯定不严谨。

有资料上会说:持久对象就是数据持久化在数据库和session缓存中的对象。我认为这句话前半句只对了“一种情况”,就是我们使用load的时候,首先load方法是hibernate从缓存中加载我们指定的ID的对象,要是没有的话就去数据库加载,那么资料上说的就是这个情况,使用loa从数据库加载对象这个步骤可以说,这时候的持久化对象是在数据库中的,但是save方法执行后的对象不能这么说,因为你事务没有提交的时候是不会操作数据库的,这是常识。

那么就会问了,insert语句到底怎么回事儿呢,我下面改了上面的代码,稍作修改,大家可以看看我是什么意思:

try {
trans = session.beginTransaction();

Employee employee1 = new Employee("feizhu","cc@qq.com",new Date());
<span style="color:#ff0000;">session.save(employee1);
System.out.println(employee1.getId());
employee1.setName("lua");
employee1.setName("java");
employee1.setName("feizhu");
trans.commit();</span>

} catch (Exception e) {
// TODO Auto-generated catch block
if(trans!=null)
trans.rollback();
e.printStackTrace();
}finally{
if(session!=null||session.isOpen())
session.close();
}
我在save之后修改了三次employee1对象的name,那么你们觉得最后会打印几条SQL呢?

结果:

Hibernate: select max(id) from employee
5
Hibernate: insert into employee (name, email, hiredate, id) values (?, ?, ?, ?)

实际上执行了一个修改数据库的语句,是啊大家会问了,执行了一个save执行了三次修改对象的操作,为什么没有三次update呢?

这里,我就说出我的理解:我们在执行save,update,delete,merge以及修改对象的操作的时候,实际上会计划一条SQL语句,而不是执行,实际的执行在commit。这句话和结果有什么关系呢?正是因为实际操作数据库是commit,所以我们可以理解hibernate是这么做的,commit之前会比较session缓存中的对象和当前对象是不是一样的(反射机制吧),如果一样就不做操作,如果不一样就计划一条update语句。

也就是说,commit前面的操作对commit来说是透明的,commit并不知道你修改了几次对象。因为我代码最后,对象的名字改回来了,session缓存发现这个OID的对象和我当前对象的所有属性一样,所以就没有计划update语句。(这里特别注意,ID一般我们不会人为修改)

然后就是说一下第一张图的结果,执行一条insert和一条update,我的猜测是hibernate先执行插入,然后对比session中的值和对象的值,发现不一样,然后计划一条update语句并执行。实际上操作了两次数据库(而先前我认为hibernate应该会为了保证性能,尽量减少数据库操作,只在缓存中改完,然后一次性提交insert到数据库)。当然这只是我的猜测。

从持久状态到离线状态只需要session关闭或者清空,这时对象本身不是空,所以不会被JVM回收,而且还会和数据库中有对应的OID。

同样,如果我们修改了这个对象的属性(一般不要改ID),并update它们就会使其进入持久化状态。就好比一个被皇上贬到边疆的官员改过自新后,得到皇上重用再次回到皇宫差不多。如果这个时候使用delete就直接进入游离状态,好比直接被皇上贬成草民了,想要回到皇宫就只能再进京赶考(在执行save)差不多。

我说的可能有点不太对,而且里面有部分是我的猜测(比如commit的时候到底操作了几次数据库),如果不对,请大家指正,初学hibernate,如果说的是对的那我表示很荣幸;如果理解的有问题,还是希望立马改过来的。

版权声明:本文为博主原创文章,未经博主允许不得转载。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息