`

Hibernate的save/update/delete等做了什么,flush做了什么

 
阅读更多

Hibernate的session.flush做了什么

---收藏笔记

这是在一次事务提交时遇到的异常。
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机制

    对hibernate的flush机制有兴趣可以看看

    hibernate_flush 深入了解

    NULL 博文链接:https://wuhaidong.iteye.com/blog/766562

    hibernate的session.flush

    博文链接:https://llying.iteye.com/blog/221702

    最全Hibernate 参考文档

    13.3. 大批量更新/删除(Bulk update/delete) 14. HQL: Hibernate查询语言 14.1. 大小写敏感性问题 14.2. from子句 14.3. 关联(Association)与连接(Join) 14.4. select子句 14.5. 聚集函数 14.6. 多态查询 14.7. ...

    hibernate操作数据库笔记

    Session对象.flush(); //将Session中的缓存内容提交到数据库 Session对象.clear(); //清空Session中的所有缓存(彻底清除会话) Session对象.beginTransaction().begin(); //开始一个事务 4.用Session对象的以下...

    hibernate总结

    d) All = delete + save-update e) Delete-orphan 只是删除父子关系的子对象 f) All-delete-orphan 父子关系的表设为此值,表示,当删除一个父对象时,将这个set容器中的子对象全部删除 2. Inverse 只能在set映射...

    Hibernate的事务处理机制和flush方法的用法.docx

    关于在使用 hibernate 在提交事务时常遇到的异常: ...其实这个异常一般都是和我们在操作session flush方法和提交事务过程中会抛出的,下面就具体结合session的事务和声明周期来具体分析下,为什么会有这样的异常;

    深入理解Hibernate中的flush机制

    主要介绍了深入理解Hibernate中的flush机制,本文是对flush机制深入研究得出的一些结论总结,需要的朋友可以参考下

    save, saveOrUpdate, persist, merge, update 区别.docx

    hibernate save()方法能够保存实体到数据库,正如方法名称save这个单词所表明的意思。我们能够在事务之外调用这个方法,这也是我不喜欢使用这个方法保存数据的原因。 假如两个实体之间有关系(例如employee表和...

    Hibernate+中文文档

    16.3. 定制SQL用来create,update和delete 16.4. 定制装载SQL 17. 过滤数据 17.1. Hibernate 过滤器(filters) 18. XML映射 18.1. 用XML数据进行工作 18.1.1. 指定同时映射XML和类 18.1.2. 只定义XML映射 18.2...

    Hibernate的Session_flush与隔离级别代码详解

    主要介绍了Hibernate的Session_flush与隔离级别代码详解,分享了相关代码示例,小编觉得还是挺不错的,具有一定借鉴价值,需要的朋友可以参考下

    hibernate 教程

    级联更新(Using cascading update()) 16.5. 结论 17. 示例:Weblog 应用程序 17.1. 持久化类 17.2. Hibernate 映射 17.3. Hibernate 代码 18. 示例:不同的映射 18.1. 雇员/雇主(Employer...

    hibernate3.2中文文档(chm格式)

    16.3. 定制SQL用来create,update和delete 16.4. 定制装载SQL 17. 过滤数据 17.1. Hibernate 过滤器(filters) 18. XML映射 18.1. 用XML数据进行工作 18.1.1. 指定同时映射XML和类 18.1.2. 只定义XML映射 18.2...

    HibernateAPI中文版.chm

    16.3. 定制SQL用来create,update和delete 16.4. 定制装载SQL 17. 过滤数据 17.1. Hibernate 过滤器(filters) 18. XML映射 18.1. 用XML数据进行工作 18.1.1. 指定同时映射XML和类 18.1.2. 只定义XML映射 18.2...

    hibernate.properties

    #hibernate.hbm2ddl.auto update #hibernate.hbm2ddl.auto validate ## specify a default schema and catalog for unqualified tablenames #hibernate.default_schema test #hibernate.default_catalog test #...

    Hibernate教程

    14.3. 大批量更新/删除(Bulk update/delete) 15. HQL: Hibernate查询语言 15.1. 大小写敏感性问题 15.2. from子句 15.3. 关联(Association)与连接(Join) 15.4. select子句 15.5. 聚集函数 15.6. 多态查询 ...

    Hibernate中文详细学习文档

    16.3. 定制SQL用来create,update和delete 16.4. 定制装载SQL 17. 过滤数据 17.1. Hibernate 过滤器(filters) 18. XML映射 18.1. 用XML数据进行工作 18.1.1. 指定同时映射XML和类 18.1.2. 只定义XML映射 18.2...

    ssh(structs,spring,hibernate)框架中的上传下载

     您可以使用Hibernate Middlegen、HIbernate Tools、Hibernate Syhchronizer等工具或手工的方式,编写Hibernate的领域对象和映射文件。其中对应T_FILE表的领域对象Tfile.java为:  代码 1 领域对象Tfile 1. ...

    Hibernate 中文 html 帮助文档

    16.3. 定制SQL用来create,update和delete 16.4. 定制装载SQL 17. 过滤数据 17.1. Hibernate 过滤器(filters) 18. XML映射 18.1. 用XML数据进行工作 18.1.1. 指定同时映射XML和类 18.1.2. 只定义XML映射 18.2. XML...

    Hibernate学习笔记

    001 Hibernate 简介(开源 O/R 映射框架) 002 第一个 Hibernate 示例 003 hibernate 主要接口介绍 004 持久对象的生命周期介绍 005 query 接口初步 006 开源 O/R 映射框架内容回顾 007 Hibernate 基本映射标签和属性...

Global site tag (gtag.js) - Google Analytics