0 0

hibernate提交时提示错误“collection was processed twice by flush()”0

实体里有个一对多的关联,保存时提示hibernate提交时提示错误“org.hibernate.AssertionFailure: collection was processed twice by flush()”,去掉那个一对多的关联就好了。实体是新增的,那个set集合里没有任何内容保存,不知道是什么原因造成这个错误?
2014年10月14日 16:08

2个答案 按时间排序 按投票排序

0 0

这个异常应该很多的朋友都遇到过,原因可能各不相同。但所有的异常都应该是在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入数据库,这样看起来也许不太美观,但很有效。

2014年10月15日 10:20
0 0

重复保存了。是不是关联的时候用了级联保存。

2014年10月14日 17:08

相关推荐

    java中常见的错误.docx

    在Java编程语言中,开发过程中会遇到各种类型的错误,这些错误通常可以分为编译错误、运行时错误和逻辑错误。以下是一些常见的Java错误及其详细解释: 1. SQL错误:在执行SQL语句时,可能会遇到`Unknown column '...

    Sekiro Shadows Die Twice v1.02 Plus 17 Trainer.zip

    Sekiro Shadows Die Twice v1.02 Plus 17 Trainer.zip

    Sonar编译问题对应:File [...] can't be indexed twice.

    在执行 Sonar 分析过程中,有时可能会遇到 "File [...] can't be indexed twice" 的错误,这通常意味着 Sonar 在处理源代码时尝试两次索引同一个文件,这可能是由于配置不正确或重叠的文件包含模式导致的。...

    You Only Look Twice Rapid Multi-Scale Object Detection In Satellite Imagery

    CV中的目标物体识别进步非常快,但是想对卫星图像进行目标识别仍然困难重重...美国研究者就提出了一种方法——You Only Look Twice,能清晰地看到卫星图像中的汽车、飞机场上的飞机及建筑物,并开放了代码(见文末)。

    Twice New Tab-crx插件

    用TWICE,待办事项列表,天气等替换新的标签页! 将新标签页替换为TWICE,待办事项列表,天气等!! 您可以使用天气制作个人仪表板,做清单,图像/视频大约两次以上。 放置旧的新标签页-仅向您显示您最近访问过的...

    You Only Look Twice Rapid Multi-Scale Object Detection In.pdf

    发表在CVR2018的YOLT算法,实现遥感图像的多尺度目标识别的论文pdf。 You Only Look Twice: Rapid Multi-Scale Object Detection In Satellite Imagery autho:Adam Van Eten

    3DMGAME-Sekiro.Shadows.Die.Twice.v1.02-v1.04.Plus.24.Trainer-FLiNG.rar

    2019.03.24更新:修正“无限物品”在开启二周目时会保留一周目剧情道具的问题。已经测试至通关,现在“无限物品”应该不会再出现其他问题了。唯一要注意是的是米要用完才能再拿,暂时关闭此功能把米都用了即可,关系...

    一般过去时被动语态.ppt

    例如,历史事件的叙述:“The Great Wall was built by Chinese people many years ago.” 或者描述研究结果:“Many trees were planted by the students yesterday.” 通过以上讲解,学习者应掌握一般过去时被动...

    ChangeScreenWin twice attuch.rar.rar

    标题中的"ChangeScreenWin twice attuch.rar.rar"可能是指一个关于在Windows系统中更改屏幕设置或切换显示器配置的教程或工具。"twice"可能暗示了这个过程涉及到两次操作,或者可能涉及到两个显示器的设置。"attuch...

    A.Collection.of.Bit.Programming.Interview.Questions.solved.in.C++

    Generate all the bit patterns from 0 to such that successive patterns differ by one bit. Chapter 25. Represent unsigned integers with variable length encoding using the continuation bit Chapter 26. ...

    H248-协议错误码

    ### H248-协议错误码详解 #### H.248协议简介 H.248协议,作为媒体网关控制协议的一种,主要用于在SCN(Switched Circuit Network)、IP、ATM、有线电视网等不同类型的网络之间进行媒体网关控制。这种协议的应用...

    Sekiro Shadows Die Twice v1.02 Plus 17 Trainer

    Sekiro Shadows Die Twice v1.02 Plus 17 Trainer

    必修3和4单句改错.doc

    10. 错误:The expanded European Union has a population of more than half a billion people, twice as big as the population of the United States.("bigger"改为"big","twice as big as"表示“是......

    中国人英文文章常范错误

    ### 中国人英文文章常见错误分析 #### 引言 随着国际学术交流的日益频繁,越来越多的中国科研人员需要使用英语撰写技术论文。然而,在实际写作过程中,由于多种因素的影响,许多作者在用英语表达时会遇到困难。...

    Sekiro: Shadows Die Twice New Tab-crx插件

    Sekiro:Shadows Die Twice New Tab Extension使您的Chrome浏览器焕然一新。 安装“ Sekiro:Shadows Die Twice”新标签,并享受Sekiro:Shadows Die Twice的精选高清图像。 它带有一些很酷的属性,这些属性可以改善...

    高考语法填空短文改错及书面表达常见错误100例汇总.doc

    - 表示年代时,需在"in"后加定冠词"the",如"The machine was invented in the 1920s"。 - "by bus"是固定搭配,不加定冠词"the"。 3. 代词的使用: - 代词的单复数、主宾格和形容词性物主代词与名词性物主代词...

    外研版八年级英语上册Module 7 unit2 She was thinking about her cat.教案 (1).doc

    通过这个故事,学生需要学习如何构造过去进行时的句子,如"was/were + 动词ing"形式。 2. **重点词汇和短语**:包括"twice", "suddenly", "pink", "pocket", "field", "deep", "while", "dry", "land", "once or ...

    C++-leetcode题解之747. Largest Number At Least Twice of Others.cpp

    747题,即Largest Number At Least Twice of Others,是leetcode上的一道经典算法题。这道题目的要求是找出数组中一个数,这个数至少是数组中其他数的两倍。具体来说,如果存在这样的数,返回这个数的索引;如果不...

Global site tag (gtag.js) - Google Analytics