@[TOC]Hibernate学习笔记Session.evict(user)方法
Hibernate学习笔记Session.evict(user)方法
首先我们要明白Session.flush(user)方法,这个方法主要做了三件事:
- 根据session缓存中的临时集合insertion中的state区中的数据(user对象),发送sql语句。
- 清空临时集合中的数据。
- 向session中的缓存map中的existsInDatebase状态设置为为true,表示数据库中已经有了这个数据。
接着来看session.evict(user);这句话表示将user对象从Session缓存中逐出,即从Session中的EntityEntries属性中逐出。这句话的作用就是清理session缓存,以防数据量过多,造成缓存的崩溃。
接着来看下面代码:
第一个例子:
/**
* 测试uuid主键生成策略
*/
public void testSave1() {
Session session = null;
Transaction tx = null;
try {
session = HibernateUtils.getSession();//通过session工厂获取session对象
tx = session.beginTransaction();//开启事务
User1 user = new User1();
user.setName("李四");
user.setPassword("123");
user.setCreateTime(new Date());
user.setExpireTime(new Date());
session.save(user);
//这里设置user的主键为userId,主键生成策略为uuid,所以调用完成save后,只是将user纳入到了session的管理,
//不会发出insert语句,但是id已经生成,session(缓存)中existsInDatebase状态为false(数据库中并不存在)
session.flush();
//调用flush,hibernate会清理缓存,1,根据临时集合执行sql
//如果数据库的隔离级别设置为为提交读,那么我们可以看到flush过的数据
//2,清空临时集合中的数据
//3,并且session中existsInDatebase状态为true(表示数据库中已经存在)
//提交事务
//默认情况下commit操作会先执行flush清理缓存,所以不用显示的调用flush
//commit后数据是无法回滚的
tx.commit();
}catch(Exception e) {
e.printStackTrace();
tx.rollback();
}finally {
HibernateUtils.closeSession(session);
}
}
在native主键生成策略中,由于主键生成策略是uuid,所以在save的时候并不会发送sql语句进行保存,所以这个时候,如果使用session.evict(user);清理缓存中的user对象,那么无法成功commit,因为hiberbate清理完临时集合之后,需要将existsInDatebase状态为true,这个时候由于evict已经将user对象从session缓存中逐出,所以hibernate找不到相关数据,所以提交失败。
/**
* 测试uuid主键生成策略中的session.evict(user)。
*/
public void testSave3() {
Session session = null;
Transaction tx = null;
try {
session = HibernateUtils.getSession();
tx = session.beginTransaction();
User1 user = new User1();
user.setName("王五");
user.setPassword("123");
user.setCreateTime(new Date());
user.setExpireTime(new Date());
//因为user的主键生成策略采用的是uuid,所以调用完成save后,只是将user纳入到了session的管理
//不会发出insert语句,但是id已经生成,session中existsInDatebase状态为false
session.save(user);
//将user对象从session(缓存)中逐出,即session的EntityEntries属性中逐出
session.evict(user);//目的是清除map中的缓存中的user对象。
//无法成功提交,因为hibernate在清理缓存时,在session的临时insertions集合中取出user对象进行insert操作后
//需要更新entityEntries属性中的existsInDatabase为true,而我们采用evict已经将user从session的entityEntries
//中逐出了,所以找不到相关数据,无法更新,抛出异常
tx.commit();
}catch(Exception e) {
e.printStackTrace();
tx.rollback();
}finally {
HibernateUtils.closeSession(session);
}
}
解决方法:在session.exict(user);语句之前使用session.flush();,这样子hibernate会清理缓存,会将user对象保存到数据库中,将session中的insertions中的user对象清除,并且设置session中existsInDatebase的状态为true,那么在commit的时候,会根据session缓存中insertion中的state中的数据发送sql语句,由于之前已经将user对象从缓存中逐出,所以这个时候找不到user对象,于是就不会发送sql语句也不会更新existsInDatebase的状态,也不会出异常了。
看代码:
/**
* 测试uuid主键生成策略
*/
public void testSave4() {
Session session = null;
Transaction tx = null;
try {
session = HibernateUtils.getSession();
tx = session.beginTransaction();
User1 user = new User1();
user.setName("王五");
user.setPassword("123");
user.setCreateTime(new Date());
user.setExpireTime(new Date());
//因为user的主键生成策略采用的是uuid,所以调用完成save后,只是将user纳入到了session的管理
//不会发出insert语句,但是id已经生成,session中existsInDatebase状态为false
session.save(user);
//flush后hibernate会清理缓存,会将user对象保存到数据库中,将session中的insertions中的user对象
//清除,并且设置session中existsInDatebase的状态为true
session.flush();
//将user对象从session中逐出,即session的EntityEntries属性中逐出
session.evict(user);
//可以成功提交,因为hibernate在清理缓存时,在session的insertions集合中无法找到user对象
//所以就不会发出insert语句,也不会更新session中的existsInDatabase的状态
tx.commit();
}catch(Exception e) {
e.printStackTrace();
tx.rollback();
}finally {
HibernateUtils.closeSession(session);
}
}
使用native主键生成策略,毫无疑问,不用在session.evict之前显示调用flush方法,也是行的通的。因为他在save的时候就已经发送了sql语句了,然后commit的时候,找不到user对象之后,不会发送sql也是无所谓的了,因为之前已经发送过了。
看代码:
/**
* 测试native主键生成策略
*/
public void testSave5() {
Session session = null;
Transaction tx = null;
try {
session = HibernateUtils.getSession();
tx = session.beginTransaction();
User2 user = new User2();
user.setName("张三11");
user.setPassword("123");
user.setCreateTime(new Date());
user.setExpireTime(new Date());
//因为user的主键生成策略为native,所以调用session.save后,将执行insert语句,返回有数据库生成的id
//纳入了session的管理,修改了session中existsInDatebase状态为true
//如果数据库的隔离级别设置为非提交读,那么我们可以看到save过的数据
session.save(user);
//将user对象从session中逐出,即session的EntityEntries属性中逐出
session.evict(user);
//可以成功提交,因为hibernate在清理缓存时,在session的insertions集合中无法找到user对象
//所以就不会发出insert语句,也不会更新session中的existsInDatabase的状态
tx.commit();
}catch(Exception e) {
e.printStackTrace();
tx.rollback();
}finally {
HibernateUtils.closeSession(session);
}
}
主键生成策略为designed的时候,分析思略和之前的一样,
看代码:
/**
* 测试assigned主键生成策略
*
*/
public void testSave7() {
Session session = null;
Transaction tx = null;
try {
session = HibernateUtils.getSession();
tx = session.beginTransaction();
User3 user = new User3();
user.setId("003");
user.setName("张三");
session.save(user);
user.setName("王五");
session.update(user);
session.flush();
User3 user3 = new User3();
user3.setId("004");
user3.setName("李四");
session.save(user3);
//Hibernate: insert into t_user3 (name, password, create_time, expire_time, user_id) values (?, ?, ?, ?, ?)
//Hibernate: update t_user3 set name=?, password=?, create_time=?, expire_time=? where user_id=?
//Hibernate: insert into t_user3 (name, password, create_time, expire_time, user_id) values (?, ?, ?, ?, ?)
//因为我们在session.udpate(user)后执行了flush,所以在清理缓存时执行flush前的sql不会生成
//sql会按照我们的意愿执行
tx.commit();
}catch(Exception e) {
e.printStackTrace();
tx.rollback();
}finally {
HibernateUtils.closeSession(session);
}
}
我们的对手不是天才,而是脚踏实地的人。