Hibernate的clear(),flush(),evict()方法详解
1.Clear 方法
无论是Load 还是 Get 都会首先查找缓存(一级缓存) 如果没有,才会去数据库查找,调用Clear() 方法,可以强制清除Session缓存。
例:
public void testClear(){
Session session = HibernateUitl.getSessionFactory().getCurrentSession();
session.beginTransaction();
Teacher t = (Teacher) session.get(Teacher.class, 3);
System.out.println(t.getName());
Teacher t2 = (Teacher) session.get(Teacher.class, 3);
System.out.println(t2.getName());
session.getTransaction().commit();
}
这里虽然用了2 个 get 方法( get 方法会立即执行 sql 语句),但因为第一次执行了会缓存一个 ID 为 3 的实体,所以虽然有 2 个 get 方法只执行一次 SQL 语句。
public void testClear(){
Session session = HibernateUitl.getSessionFactory().getCurrentSession();
session.beginTransaction();
Teacher t = (Teacher) session.get(Teacher.class, 3);
System.out.println(t.getName());
session.clear();//这里不clear只会执行一次sql语句,有clear会执行2次
Teacher t2 = (Teacher) session.get(Teacher.class, 3);
System.out.println(t2.getName());
session.getTransaction().commit();
}
这里在第2 次 get 前执行 session.clear(), 我们把 hibernate show_sql 出来,它就会执行 2 次 sql 语句了。 所以session.clear() 会清除缓存。
2.Flush方法
可以强制进行从内存到数据库的同步。
例:
@Test
/**
* flush 强制与数据库同步
*/
public void testFlush(){
Session session = HibernateUitl.getSessionFactory().getCurrentSession();
session.beginTransaction();
Teacher t = (Teacher) session.get(Teacher.class, 3);
t.setName("yyy");
t.setName("yyyyy");
session.getTransaction().commit();
}
看这段代码,我们setName() 2 次, 但程序只会更改数据库一次,在 commit 时。
@Test
/**
* flush 强制与数据库同步
*/
public void testFlush(){
Session session = HibernateUitl.getSessionFactory().getCurrentSession();
session.beginTransaction();
Teacher t = (Teacher) session.get(Teacher.class, 3);
t.setName("yyy");
session.flush();//有flush会执行2次UPDAE,没有会只执行一次
t.setName("yyyyy");
session.getTransaction().commit();
}
我们在第2 次 setName ()时 执行 session.flush().
再看hibernate 执行的 sql 语句
Hibernate:
update
Teacher
set
birthday=?,
name=?,
title=?
where
id=?
Hibernate:
update
Teacher
set
birthday=?,
name=?,
title=?
where
id=?
执行了2 次 Update。所以看出来flush 方法会强制与数据库同步。Flush方法是可以设置的,也就是 fulsh 什么时候执行是可以设置的 在session.beginTransaction 前设置 FlushMode
session.setFlushMode(FlushMode.Always|AUTO|COMMIT|NEVER|MANUAL)
FlushMode有 5 个值可选
Always:任何代码都会
Flush
AUTO:默认方式
–
自动
Commit:COMMIT时
Never:始终不
MANUAL:手动方式
设置FlushMode 有个好处是可以节省开销,比如默认 session 只做查询时,就可以不让他与数据库同步了。
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
。

浙公网安备 33010602011771号