Hibernate的evict(obj) clear() flush()
问:
先创建一个Student,然后调用session.save方法,然后再调用evict方法把Student对象清除出缓存,再提交事务,
可是会报错:Exception in thread "main" org.hibernate.AssertionFailure: possible nonthreadsafe access to session
但是如果我用的不是evict方法,而是clear方法用来清除缓存的话,程序没有错。
答:
session.evict(obj),从session缓存(EntityEntries属性)中逐出该对象
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。
- 但是与commit同时使用,会抛出异常
- session = HibernateUtils.getSession();
- tx = session.beginTransaction();
- User1 user = new User1();
- user.setName("李四");
- user.setPassword("123");
- user.setCreateTime(new Date());
- user.setExpireTime(new Date());
- //利用Hibernate将实体类对象保存到数据库中
- //因为user主键生成策略采用的是uuid,所以调用完成save后,只是将user纳入session的管理
- //不会发出insert语句,但是id已经生成,session中的existsInDatabase状态为false
- session.save(user);
- session.evict(user);//从session缓存(EntityEntries属性)中逐出该对象
- //无法成功提交,因为hibernate在清理缓存时,在session的临时集合(insertions)中取出user对象进行insert操作后需要更新entityEntries属性中的existsInDatabase为true,而我们采用evict已经将user从session中逐出了,所以找不到相关数据,无法更新,抛出异常。
- tx.commit();
- 解决在逐出session缓存中的对象不抛出异常的方法:
- 在session.evict()之前进行显示的调用session.flush()方法就可以了。
- //利用Hibernate将实体类对象保存到数据库中
- //因为user主键生成策略采用的是uuid,所以调用完成save后,只是将user纳入session的管理
- //不会发出insert语句,但是id已经生成,session中的existsInDatabase状态为false
- session.save(user);
- //flush后hibernate会清理缓存,会将user对象保存到数据库中,将session中的insertions中的user对象清除,并且会设置session中的existsInDatabase状态为true
- session.flush();
- session.evict(user);//从session缓存(EntityEntries属性)中逐出该对象
- //可以成功提交,因为hibernate在清理缓存时,在Session的insertions中集合中无法找到user对象所以不会发出insert语句,也不会更新session中existsInDatabase的状态。
- tx.commit();
session.evict(obj),会把指定的缓冲对象进行清除
session.clear(),把缓冲区内的全部对象清除,但不包括操作中的对象
隔离级别 脏读 不可重复读 幻读
ReadUncommitted Y Y Y
ReadCommitted N Y Y
RepeatableRead N N Y
Serializable N N N
ReadCommited是oracle的默认隔离级别。可以通过悲观锁,消除不可重复读。
RepeatableRead是Mysql的默认级别。
session flush方法主要做了两件事:
* 清理缓存
* 执行sql
session在什么情况下执行flush
* 默认在事务提交时
* 显示的调用flush
* 在执行查询前,如:iterate
hibernate按照save(insert),update、delete顺序提交相关操作
- /**
- * 测试uuid主键生成策略
- */
- public void testSave1() {
- 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会清理缓存,执行sql
- //如果数据库的隔离级别设置为为提交读,那么我们可以看到flush过的数据
- //并且session中existsInDatebase状态为true
- session.flush();
- //提交事务
- //默认情况下commit操作会先执行flush清理缓存,所以不用显示的调用flush
- //commit后数据是无法回滚的
- tx.commit();
- }catch(Exception e) {
- e.printStackTrace();
- tx.rollback();
- }finally {
- HibernateUtils.closeSession(session);
- }
- }
- /**
- * 测试native主键生成策略
- */
- public void testSave2() {
- Session session = null;
- Transaction tx = null;
- try {
- session = HibernateUtils.getSession();
- tx = session.beginTransaction();
- User2 user = new User2();
- user.setName("张三1");
- 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);
- tx.commit();
- }catch(Exception e) {
- e.printStackTrace();
- tx.rollback();
- }finally {
- HibernateUtils.closeSession(session);
- }
- }
- /**
- * 测试uuid主键生成策略
- */
- 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);
- //无法成功提交,因为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);
- }
- }
- /**
- * 测试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主键生成策略
- */
- 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);
- }
- }
- /**
- * 测试assigned主键生成策略
- *
- */
- public void testSave6() {
- Session session = null;
- Transaction tx = null;
- try {
- session = HibernateUtils.getSession();
- tx = session.beginTransaction();
- User3 user = new User3();
- user.setId("001");
- user.setName("张三");
- session.save(user);
- user.setName("王五");
- session.update(user);
- User3 user3 = new User3();
- user3.setId("002");
- user3.setName("李四");
- session.save(user3);
- //Hibernate: insert into t_user3 (name, password, create_time, expire_time, user_id) values (?, ?, ?, ?, ?)
- //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按照save(insert),update、delete顺序提交相关操作
- tx.commit();
- }catch(Exception e) {
- e.printStackTrace();
- tx.rollback();
- }finally {
- HibernateUtils.closeSession(session);
- }
- }
- /**
- * 测试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);
- }
- }
Hibernate的flush机制:http://sind.iteye.com/blog/255429

浙公网安备 33010602011771号