一、一级缓存和快照
一级缓存:
每次hibernate与数据库打交道时,都是通过session对操作对象取得关联,然后进行操作,具体过程如下:
1.首先session将一个对象加入自己的管理范围内,其实也就是把该对象放入自己的一级缓存中,例如,session.save(xxx);这个语句就是将xxx保存在自己的一级缓存中,等待事务提交后,hibernate才真正的发sql语句,对数据库进行操作。注意:session进行操作的时候,是将对象加入自己的一级缓存,并不是就直接跟数据库打交道了。
2.在一级缓存中会做些什么事情呢?为什么能够知道是发insert、还是update又或者delete呢?那这里就要提到一个快照的概念了,讲讲内部是什么原理。
session.save()
User user = new User();
user.setUsername("xiaoming");
user.setPassword("123");
session.save(user);//加入了一级缓存,这其中做了什么事情呢?看图
user.setUsername("baibai");//那这里改了user的属性,具体会发生什么事情呢
//提交事务
tx.commit();
//关闭session,持久化对象就会变为脱管状态。
session.close();
发送的sql语句
//insert语句
Hibernate:
insert into user (username, password, c_id) values (?, ?, ?)
//update语句
Hibernate:
update user set username=?, password=?, c_id=? where id=?
session.get()
//通过图中分析,通过select语句查询,并且user会在session的一级缓存中,
User user =(User)session.get(User.class, "8");
//会发出update语句。
user.setUsername("heihei");
//select语句
Hibernate:
select
user0_.id as id0_0_,
user0_.username as username0_0_,
user0_.password as password0_0_,
user0_.c_id as c4_0_0_
from
user user0_
where
user0_.id=?
//update语句
Hibernate:
update
user
set
username=?,
password=?,
c_id=?
where
id=?
通过上面的例子,我们应该知道,并不是session一操作对象,就立马会发sql语句,而是先将其保存到自己内部的一级缓存中,如果不手动刷出缓存,就会等待事务提交时刷出缓存,然后再进行发送sql语句,对数据库进行操作。并且其中有一个快照区需要知道是干嘛的,理解了快照区,就可以知道每次会发送几条sql语句。注意一点,session每次操作的是在一级缓存中的对象,不会操作快照区中的对象,快照区只是用来当作比较的一个地方,当对象加入一级缓存之后,外部在怎么改变,也只是改变快照区中的数据,并不是改变一级缓存中对象的属性。这点就记清楚。
二、hibernate中对象的三种状态
瞬时状态(transient):
1.在数据库表中,没有任何一条数据与它对应
2.不在session的缓存中
持久状态(托管)persistent:在session的缓存中
1.在session的缓存中(注意,很多书上都觉得持久化状态都在数据库表中有相应记录,这个是错误的,比如一个瞬时状态的对象刚被session.save(),事务还没提交,此时瞬时状态就已经变为持久化状态了,但是在数据库中还没有记录)
2.在数据库中可能有记录
游离(脱管)detached状态 :从session的缓存中移除出来了
1.是从session缓存中出来的,也就是从持久状态转变而来的,没有别的方式能到达游离(脱管)状态,只有这一种。
2.不在session的管理范围内
3.在数据库中有记录
误区1、很多书上觉得通过id值就可以判断对象是在哪个状态,比如说,没有id值,就是瞬时状态,有id并且在session管理范围内,就是持久状态,有id不在session范围内就是脱管状态,
解释:从上面的解释来看,瞬时状态变为脱管状态,只要加上id就行了,但是从官方图来说,瞬时状态不能直接变为游离状态,而游离状态可以通过delete直接到达瞬时状态(其实说直接,也是先将游离状态的对象加入到session缓存中变为持久状态,然后delete,在变为瞬时状态而已。),那么上面所用的通过有没有id值来判断三种状态就是有偏差的,可以这么理解,瞬时状态在数据库中就一定没有对应的记录,而游离状态一定是通过持久状态转变而来的,并且在数据库中可能有记录(没有记录是因为多线程,有别的程序把那条记录给删除了,所以一般就觉得是有记录的),所以瞬时状态和游离状态的区分点就在:从什么转变而来的,如果直接new的,那么就是瞬时状态对象,如果从持久态转变的,那么就是游离状态。
误区2:很多书上或者博客中会说识别是不是持久化状态,是看在session缓存内,还有就是在数据库中有记录。
解释:这里的前半句对,但是后半句错误,持久状态可能在数据库中有记录,也可能在数据库中没有记录,说说没有记录的时候吧,就是当session.save保存瞬时状态时,事务还没有提交,对象还只是在session的一级缓存中,数据库中就没有该记录,但此时它就已经是持久状态对象了。
误区3:认为session一操作,就会发出sql语句,这个上面已经说明白了,应该很清楚了。
怎么区分这三种状态:
1、如果是在session缓存中,那么就一定是持久状态
2、如果是刚new出来的对象,那么就肯定是瞬时状态
3、如果是从session缓存中出来的,也就是通过一些session.clear、ecivt等操作,清除了缓存的,那么就是游离状态,只有瞬时状态能确定数据库中没有对应记录,其他两个状态,都是不确定数据库中是否有对应记录
一切都以官方给出的图为准,其他的快速识别状态的方法,我认为是不可取的,自己还是没弄明白其中的道理,也就永远没把握自己是不是对的。