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)这次登记的动作,这样就不会出现主键冲突的状况。
分享到:
相关推荐
`Session.flush()`方法是一个关键的操作,它强制Hibernate将内存中的对象状态同步到数据库,确保数据的一致性。这篇博客深入探讨了`Session.flush()`的工作原理和应用场景。 `Session`在Hibernate中主要有以下职责...
在Java的Hibernate框架中,一级缓存是Session对象内置的缓存机制,它的存在是为了优化数据库操作,减少对数据库的直接访问,从而提高应用程序的性能。一级缓存的生命周期与Session对象相同,也就是说,只要Session...
根据提供的文件信息,我们可以深入探讨Hibernate中的几个关键概念与操作,包括`Session.flush()`方法的使用、不同主键生成策略下的保存操作等。 ### Hibernate Session.flush() 方法详解 #### 一、基本概念 在...
通过`openSession()`方法创建Session实例,`save()`, `update()`, `delete()`等方法用于持久化操作,`flush()`和`clear()`方法则用来管理缓存和事务。 3. **Criteria 查询**:除了传统的HQL(Hibernate Query ...
4. **关闭Session**:当Session被关闭时,Hibernate会自动执行Flush操作,以确保所有未提交的更改都被持久化到数据库中。 #### 错误示例分析 在给定的部分内容中,提到了一个典型的错误场景,其中包含了一个常见的...
#hibernate.transaction.flush_before_completion ## Enable automatic session close at the end of transaction ## (This setting is relevant with or without the Transaction API) #hibernate.transaction....
总结起来,Hibernate的Configuration管理环境设置,SessionFactory是创建Session的工厂,而Session则是实际执行数据库操作的接口。理解这三个概念及其相互关系,对于有效使用Hibernate进行数据库操作至关重要。在...
session.flush(); session.clear(); } } transaction.commit(); session.close(); ``` 以上就是使用Hibernate连接MySQL数据库的基本步骤和操作示例。注意,实际应用中还需要考虑事务管理、异常处理、性能...
我们可以设置一个合理的 JDBC 批处理大小,例如 hibernate.jdbc.batch_size 20,然后在一定间隔对 Session 进行 flush() 和 clear()。这样可以避免内存溢出错误,并提高性能。 在批量插入数据时,我们可以使用以下...
当我们在Session上进行操作时,这些操作不会立即反映到数据库中,而是被缓存起来,直到调用`flush()`方法或者事务提交时才会真正执行。 在开始使用Session之前,需要配置Hibernate的环境,包括创建SessionFactory,...
1. CRUD操作:通过Session接口实现增删改查,如`session.save()`、`session.get()`、`session.update()`和`session.delete()`。 2. HQL查询:使用Query接口进行基于Hibernate Query Language的查询,支持复杂条件和...
session.flush(); session.clear(); } } tx.commit(); } catch (Exception e) { if (tx != null) tx.rollback(); e.printStackTrace(); } finally { session.close(); } ``` #### 五、常见问题解答 **...
这是因为 Hibernate 的 Session 有一个一级缓存,会将所有对象存储在内存中。如果不及时清空缓存,内存将会溢出。 解决方法是使用事务和 flush() 方法来清空缓存。例如: ```java Transaction tx = session.begin...
session.flush(); session.clear(); } } tx.commit(); session.close(); ``` 对于更新和删除操作,可以使用`scroll()`方法,这在Hibernate 2.1.6或更高版本中是支持的。`scroll()`方法返回一个`...
session.flush(); // 提交事务 ``` **7. 删除数据** 通过Session的delete()方法删除记录。 ```java User user = (User) session.get(User.class, userId); session.delete(user); session.flush(); ``` **8. 显示...
当达到特定条件或手动调用`Session.flush()`时,Hibernate会将这些变更持久化到数据库。 Flush过程主要包括以下步骤: 1. **对象状态检查**:Hibernate会检查Session中的所有对象,判断它们是否需要被更新、插入...
session.flush(); tx.commit(); session.close(); ``` - **JTA事务管理**:更高级的一种事务管理模式,用于分布式环境中多个资源管理器之间的事务协调。这种方式通常与EJB容器一起使用。通过配置`hibernate....
本示例将深入探讨Hibernate Session的生命周期及其使用,帮助你更好地理解和运用这个强大的工具。 Hibernate Session是Hibernate的核心接口,它是与数据库交互的主要接口。Session对象负责管理实体对象的状态,包括...
在业务逻辑处理中,通常使用Session的beginTransaction、save/merge、flush和commit等方法来完成数据库操作。 3. **Query和Criteria API**:提供两种方式执行查询,一是HQL(Hibernate Query Language),类似于SQL...
- 更新:`Session.update()`或直接修改对象后调用`Session.flush()`。 - 删除:`Session.delete()`方法删除对象。 - 加载与检索:`Session.get()`或`Session.load()`加载指定ID的对象,`Session.createQuery()`...