随着Hibernate在Java开发中的广泛应用,我们在使用Hibernate进行对象持久化操作中也遇到了各种各样的问题。这些问题往往都是我们对Hibernate缺乏了解所致,这里我讲个我从前遇到的问题及一些想法,希望能给大家一点借鉴。

这是在一次事务提交时遇到的异常。
      an assertion failure occured (this may indicate a bug in Hibernate, but is more likely due to unsafe use of the session)
net.sf.hibernate.AssertionFailure: possible nonthreadsafe access to session
注:非possible non-threadsafe access to the session (那是另外的错误,类似但不一样)
      这个异常应该很多的朋友都遇到过,原因可能各不相同。但所有的异常都应该是在flush或者事务提交的过程中发生的。这一般由我们在事务开始至事务提交的过程中进行了不正确的操作导致,也会在多线程同时操作一个Session时发生,这里我们仅仅讨论单线程的情况,多线程除了线程同步外基本与此相同。
      至于具体是什么样的错误操作那?我给大家看一个例子(假设Hibernate配置正确,为保持代码简洁,不引入包及处理任何异常) 
SessionFactory sf = new Configuration().configure().buildSessionFactory() ;
Session s = sf.openSession();
Cat cat = new Cat();
Transaction tran = s.beginTransaction(); (1)
s.save(cat); (2)(此处同样可以为update delete)
s.evict(cat); (3)
tran.commit(); (4)
s.close();(5)
      这就是引起此异常的典型错误。我当时就遇到了这个异常,检查代码时根本没感觉到这段代码出了问题,想当然的认为在Session上开始一个事务,通过Session将对象存入数据库,再将这个对象从Session上拆离,提交事务,这是一个很正常的流程。如果这里正常的话,那问题一定在别处。 
        问题恰恰就在这里,我的想法也许没有错,但是一个错误的论据所引出的观点永远都不可能是正确的。因为我一直以为直接在对数据库进行操作,忘记了在我与数据库之间隔了一个Hibernate,Hibernate在为我们提供持久化服务的同时,也改变了我们对数据库的操作方式,这种方式与我们直接的数据库操作有着很多的不同,正因为我们对这种方式没有一个大致的了解造成了我们的应用并未得到预先设想的结果。
那Hibernate的持久化机制到底有什么不同那?简单的说,Hibernate在数据库层之上实现了一个缓存区,当应用save或者update一个对象时,Hibernate并未将这个对象实际的写入数据库中,而仅仅是在缓存中根据应用的行为做了登记,在真正需要将缓存中的数据flush入数据库时才执行先前登记的所有行为。
在实际执行的过程中,每个Session是通过几个映射和集合来维护所有与该Session建立了关联的对象以及应用对这些对象所进行的操作的,与我们这次讨论有关的有entityEntries(与Session相关联的对象的映射)、insertions(所有的插入操作集合)、deletions(删除操作集合)、updates(更新操作集合)。下面我就开始解释在最开始的例子中,Hibernate到底是怎样运作的。
(1)生成一个事务的对象,并标记当前的Session处于事务状态(注:此时并未启动数据库级事务)。
(2)应用使用s.save保存cat对象,这个时候Session将cat这个对象放入entityEntries,用来标记cat已经和当前的会话建立了关联,由于应用对cat做了保存的操作,Session还要在insertions中登记应用的这个插入行为(行为包括:对象引用、对象id、Session、持久化处理类)。
(3)s.evict(cat)将cat对象从s会话中拆离,这时s会从entityEntries中将cat这个对象移出。
(4)事务提交,需要将所有缓存flush入数据库,Session启动一个事务,并按照insert,update,……,delete的顺序提交所有之前登记的操作(注意:所有insert执行完毕后才会执行update,这里的特殊处理也可能会将你的程序搞得一团糟,如需要控制操作的执行顺序,要善于使用flush),现在cat不在entityEntries中,但在执行insert的行为时只需要访问insertions就足够了,所以此时不会有任何的异常。异常出现在插入后通知Session该对象已经插入完毕这个步骤上,这个步骤中需要将entityEntries中cat的existsInDatabase标志置为true,由于cat并不存在于entityEntries中,此时Hibernate就认为insertions和entityEntries可能因为线程安全的问题产生了不同步(也不知道Hibernate的开发者是否考虑到例子中的处理方式,如果没有的话,这也许算是一个bug吧),于是一个net.sf.hibernate.AssertionFailure就被抛出,程序终止。

我想现在大家应该明白例子中的程序到底哪里有问题了吧,我们的错误的认为s.save会立即的执行,而将cat对象过早的与Session拆离,造成了Session的insertions和entityEntries中内容的不同步。所以我们在做此类操作时一定要清楚Hibernate什么时候会将数据flush入数据库,在未flush之前不要将已进行操作的对象从Session上拆离。

对于这个错误的解决方法是,我们可以在(2)和(3)之间插入一个s.flush()强制Session将缓存中的数据flush入数据库(此时Hibernate会提前启动事务,将(2)中的save登记的insert语句登记在数据库事务中,并将所有操作集合清空),这样在(4)事务提交时insertions集合就已经是空的了,即使我们拆离了cat也不会有任何的异常了。
前面简单的介绍了一下Hibernate的flush机制和对我们程序可能带来的影响以及相应的解决方法,Hibernate的缓存机制还会在其他的方面给我们的程序带来一些意想不到的影响。看下面的例子:
(name为cat表的主键)
Cat cat = new Cat();
cat.setName(“tom”);
s.save(cat);
cat.setName(“mary”);
s.update(cat);(6)
Cat littleCat = new Cat();
littleCat.setName(“tom”);
s.save(littleCat);
s.flush();
这个例子看起来有什么问题?估计不了解Hibernate缓存机制的人多半会说没有问题,但它也一样不能按照我们的思路正常运行,在flush过程中会产生主键冲突,可能你想问:“在save(littleCat)之前不是已经更改cat.name并已经更新了么?为什么还会发生主键冲突那?”这里的原因就是我在解释第一个例子时所提到的缓存flush顺序的问题,Hibernate按照insert,update,……,delete的顺序提交所有登记的操作,所以你的s.update(cat)虽然在程序中出现在s.save(littleCat)之前,但是在flush的过程中,所有的save都将在update之前执行,这就造成了主键冲突的发生。
这个例子中的更改方法一样是在(6)之后加入s.flush()强制Session在保存littleCat之前更新cat的name。这样在第二次flush时就只会执行s.save(littleCat)这次登记的动作,这样就不会出现主键冲突的状况。再看一个例子(很奇怪的例子,但是能够说明问题)
Cat cat = new Cat();
cat.setName(“tom”);
s.save(cat); (7)
s.delete(cat);(8)
cat.id=null;(9)
s.save(cat);(10)
s.flush();
这个例子在运行时会产生异常net.sf.hibernate.HibernateException: identifier of an instance of Cat altered from 8b818e920a86f038010a86f03a9d0001 to null这里例子也是有关于缓存的问题,但是原因稍有不同:(7)和(2)的处理相同。(8)Session会在deletions中登记这个删除动作,同时更新entityEntries中该对象的登记状态为DELETED。(9)Cat类的标识符字段为id,将其置为null便于重新分配id并保存进数据库。 
(10)此时Session会首先在entityEntries查找cat对象是否曾经与Session做过关联,因为cat只改变了属性值,引用并未改变,所以会取得状态为DELETED的那个登记对象。由于第二次保存的对象已经在当前Session中删除,save会强制Session将缓存flush才会继续,flush的过程中首先要执行最开始的save动作,在这个save中检查了cat这个对象的id是否与原来执行动作时的id相同。不幸的是,此时cat的id被赋为null,异常被抛出,程序终止(此处要注意,我们在以后的开发过程尽量不要在flush之前改变已经进行了操作的对象的id)。 
这个例子中的错误也是由于缓存的延时更新造成的(当然,与不正规的使用Hibernate也有关系),处理方法有两种: 
1、在(8)之后flush,这样就可以保证(10)处save将cat作为一个全新的对象进行保存。 
2、删除(9),这样第二次save所引起的强制flush可以正常的执行,在数据库中插入cat对象后将其删除,然后继续第二次save重新插入cat对象,此时cat的id仍与从前一致。
这两种方法可以根据不同的需要来使用,呵呵,总觉得好像是很不正规的方式来解决问题,但是问题本身也不够正规,只希望能够在应用开发中给大家一些帮助,不对的地方也希望各位给与指正。
  总的来说,由于Hibernate的flush处理机制,我们在一些复杂的对象更新和保存的过程中就要考虑数据库操作顺序的改变以及延时flush是否对程序的结果有影响。如果确实存在着影响,那就可以在需要保持这种操作顺序的位置加入flush强制Hibernate将缓存中记录的操作flush入数据库,这样看起来也许不太美观,但很有效。

浅谈Hibernate的flush机制相关推荐

  1. 浅谈ASP.NET内部机制(五)

    浅谈ASP.NET内部机制(五) 前言:本章要谈页面生命周期了,过程挺多的,但是一点都不难.不信可以看看.我尽量的讲的平实一些,而且理解页面的生命周期对喜欢开发自定义控件和组件的朋友是很有帮助的. 系 ...

  2. 浅谈 LiveData 的通知机制

    LiveData 和 ViewModel 是 Google 官方的 MVVM 架构的一个组成部分.巧了,昨天分析了一个问题是 ViewModel 的生命周期导致的.今天又遇到了一个问题是 LiveDa ...

  3. 浅谈Hibernate中的几个查询

    浅谈Hibernate中的几个查询 一.load和get方法的区别 1.load在查询时,先从一级缓存中寻找与数据索引对应的实体对象,然后构建并返回一个代理对象,当我们真正使用这个代理对象的时候,这时 ...

  4. Java hibernate假外键_浅谈hibernate急迫加载问题(多重外键关联)

    数据库结构如下 strategy中有外键member_id(关联member表)外键strategy_category(关联category表)而member表中有外键position_id(关联po ...

  5. ajax机制 缓存,浅谈Ajax的缓存机制

    浅谈Ajax的缓存机制 Ajax的缓存机制和浏览器处理资源时的缓存机制是一样的. 三条简单规则: 只要是URL相同的GET请求,浏览器会使用缓存(当然还要看服务器的Cache-Control/Expi ...

  6. js 单页面ajax缓存策略,浅谈ajax的缓存机制---IE浏览器方面

    这篇文章主要介绍了IE浏览器关于ajax的缓存机制,文中给大家提到了Ajax解决浏览器的缓存问题,解决方法有很多种.有一定的参考价值,有需要的朋友可以参考一下,希望对你们有所帮助. IE浏览器对于同一 ...

  7. 浅谈Hibernate批量操作

    在正式地介绍hibernate批量操作之前,先给大家普及一个hibernate重要的成员,即hibernate一级缓存,这个一级缓存不像二级缓存那样可插拔似的,是无条件使用的,这个缓存最大的一个作用就 ...

  8. tcp retransmission 出现的原因_浅谈TCP的keepalive机制

    相关背景: hbase集群大量regionserver节点进程挂掉,排查log发现每个节点上的有大量的和datanode建立连接失败的报错信息,进一步排查是大量的Too Many Open Files ...

  9. 若要加载模块二进制_春哥说 | 浅谈NodeJs的模块机制-2

    ★ 目录 ★ 01 Node的模块实现概述 02 优先从缓存中加载 03 路径分析和文件定位 Node的模块实现概述 Nodejs再集成CommonJs的模块机制的规范时进行了取舍,同时增加了特性. ...

  10. 浅谈runtime运行时机制

    由于OC是运行时语言,只有在程序运行时,才会去确定对象的类型,并调用类与对象相应的方法.利用runtime机制让我们可以在程序运行时动态修改类.对象中的所有属性.方法. 下面就介绍运行时一种很简单的使 ...

最新文章

  1. linux获取最高权限并取消_Linux 更新glibc 漏洞 可以获取最高权限
  2. 基于vue-cli,做个nuxt脚手架~
  3. 关于Block Formatting Context--BFC和IE的hasLayout
  4. 初中职校计算机学什么,职高有哪些专业 初中生毕业上职高学什么好
  5. OpenCV直方图均衡Histogram Equalization
  6. mysql or 创建索引_Mysql索引优化
  7. android之seekbar
  8. 机器学习面试--决策树
  9. 《构建高可用Linux服务器 第3版》—— 1.5 Linux服务器的优化
  10. 使用python进行收据搜集示例之feature_engineering_example
  11. 计算机tpm管理,【教程】安装 Windows 11 的三种方法,绕过TPM2.0
  12. python 头条新闻机器人_使用今日头条web版API实现的头条机器人
  13. 英雄榜 此服务器当前正在维护中,《天下3》2021年全新山海经版本“再战黄泉”震撼登场!——网易《天下3》官方网站...
  14. 上线不到两个月,昇腾AI助推“中国算力网”再添新节点
  15. 【STM32】 电解电容
  16. springboot框架开发的多商家在线商城系统
  17. 区块链共享数据架构设计
  18. 自动化立体仓库AGV小车控制系统
  19. 使用BBED恢复DELETE的数据
  20. Unity学习笔记:个人学习项目《疯狂天才埃德加》纠错文档

热门文章

  1. 为什么大学没有前端课程?
  2. 柴静——《穹顶之下》的雾霾调查读后感
  3. mysql 中的 SCHEMATA 表
  4. it试用评估_it试用期员工自我评价
  5. 银行系统需要处理的信息
  6. Python读取文件找出重复元素
  7. 关于文章手把手教你使用stata做竞争风险模型的一些其他操作
  8. matlab入门——矩阵(运算、编辑、高维矩阵、对角矩阵)
  9. 企业微信之网页授权登录
  10. B站黑马程序员Oracle学习——Oracle基础