`

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机制

    当开发人员调用Session的`save()`, `update()`, `delete()`等方法时,Hibernate并不会立即执行SQL语句,而是将这些操作记录在其内部的一系列映射和集合中,如`entityEntries`, `insertions`, `deletions`, `updates`...

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

    `flush`是强制Hibernate将缓存中的对象状态同步到数据库的操作,它会按照保存(save)、更新(update)和删除(delete)的顺序执行。在默认情况下,Hibernate会在事务提交前自动调用`flush`,但有时我们需要手动调用...

    hibernate连接数据[Mysql]的代码实例

    **hibernate连接数据[Mysql]的代码实例** 在Java应用程序中,Hibernate是一个非常流行的ORM(Object-Relational Mapping)框架,它提供了强大的数据库操作能力,简化了Java与关系型数据库如MySQL之间的交互。本实例将...

    Hibernate学习笔记(培训学习时的笔记)

    2. Save/Update/Delete:Session提供了对实体的增删改操作,如save(), saveOrUpdate(), update(), delete()。 3. Flush:Session会定期或者在某些操作后自动将内存中的改变同步到数据库,也可以手动调用flush()方法...

    Hibernate语句

    以上只是Hibernate使用的基础介绍,实际应用中还包括实体关系映射、缓存机制、查询优化等多个方面。通过学习和熟练掌握Hibernate,可以极大地提高开发效率,降低数据库操作的复杂性。对于更深入的探讨,建议查阅官方...

    hibernate操作数据库笔记

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

    基于hibernate的baseDao

    - `save()`:保存对象到数据库,对应Hibernate的`Session.save()`或`Session.persist()`方法。 - `update()`:更新数据库中的对象,对应Hibernate的`Session.update()`方法。 - `delete()`:删除数据库中的对象,...

    hibernate入门实例操作步骤

    使用`Session`进行CRUD操作,如`save()`、`update()`、`delete()`和`load()`/`get()`。记得每次操作后都要调用`flush()`和`close()`方法。 **二、Eclipse自动生成配置** 1. **新建Hibernate项目** 在Eclipse中...

    hibernate源代码的zip文件

    Session是短暂的,每次数据库操作后应关闭,它提供了open、close、save、update、delete、flush、clear等方法,以及Criteria、HQL和SQL查询。 五、持久化操作 Hibernate支持对象的持久化操作,如增删改查。save()...

    Hibernate编程源代码

    在`HibernateDemo`中,会看到`Session`的`openSession()`、`beginTransaction()`、`save() / persist() / update() / delete()`、`flush()`和`commit()`等方法的使用。 7. **Criteria查询** Hibernate提供了 ...

    第一个hibernate程序及解释

    **hibernate 程序及解释** 在Java开发中,Hibernate是一个强大的对象关系映射(ORM)框架,它极大地简化了数据库操作。...通过这个起点,你可以进一步探索Hibernate的高级特性,如懒加载、级联操作、缓存机制等。

    HibernateDemo HibernateDemo

    - **Update(更新)**:调用`update()`方法更新对象的属性,然后`flush()`提交到数据库。 - **Delete(删除)**:`delete()`方法用于根据主键删除对象,或通过查询结果删除多条记录。 **5. Hibernate 的缓存机制** ...

    hibernate3.6.jar

    通过`openSession()`方法创建Session实例,`save()`, `update()`, `delete()`等方法用于持久化操作,`flush()`和`clear()`方法则用来管理缓存和事务。 3. **Criteria 查询**:除了传统的HQL(Hibernate Query ...

    hibernate--3.Hibernate数据持久化(通过 Session 操纵对象)

    SessionFactory由Configuration实例创建,配置文件通常为`hibernate.cfg.xml`,其中包含了数据库连接信息、实体类映射等设置。 接下来,我们可以通过SessionFactory获取Session实例。然后,可以使用Session的`save...

    西安领航核心项目Hibernate部分重点、难点总结

    ### Save、Update、Delete方法的执行顺序及Flush方法 在事务中,Hibernate会按自己的逻辑顺序执行SQL语句,通常遵循Insert、Update、Delete的顺序,这可能与代码逻辑不符。使用`session.flush()`可强制执行当前点的...

    hibernate对象关系映射案例demo

    3. **更新(Update)**:调用`merge()`或直接修改对象属性后`flush()`。 4. **删除(Delete)**:`delete()`或`remove()`。 六、 Session与Transaction 在进行数据库操作前,需要创建Session并开启Transaction。...

    hibernate笔记

    - **hibernate.cfg.xml**: 这是 Hibernate 的主配置文件,包含了数据库连接信息、缓存设置等关键参数。 - **实体类的映射文件**: 通常以 `.hbm.xml` 结尾,定义了 Java 类与数据库表之间的映射规则。 3. **实体...

    Hibernate官方中文参考手册下载

    接着,配置hibernate.cfg.xml文件,指定数据库连接参数、方言、实体扫描路径等信息。 **3. 实体类与映射文件** 在Hibernate中,每个Java类都可以映射为数据库中的一个表,通过注解或者XML文件进行配置。注解如@...

    Hibernate框架

    此外,`Session`还负责对象的保存、更新和删除操作,如`save()`, `update()`, 和`delete()`方法。 查询是任何ORM框架的关键部分。Hibernate提供了一种称为HQL(Hibernate Query Language)的语言,它是SQL的面向...

Global site tag (gtag.js) - Google Analytics