Hibernate的FlushMode与session.flush()

理解Hibernate缓存机制与刷新模式

我们借助于Session来操作数据库,Session是有缓存的(在hibernate中称为一级缓存),当我们调用session.save()或者update()等的方法的时候,hibernate并不一定会马上将修改同步到数据库,而是先将这些信息存储在session缓存中,由hibernate自己在合适的时候将缓存中的数据,同步刷新到数据库中。正是由于hibernate的这种缓存机制,在同一个session中多次修改一个记录,最终只会向数据库发出一条update语句。由于session缓存以及脏数据检查机制,能够帮助我们尽可能少地发出SQL语句。


hibernate提供了FlushMode接口,能够让我们干预hibernate将脏数据同步到数据库的时机。Session.flush()会触发hibernate将数据同步到数据库。可以通过session.setFlushMode()来修改刷新模式。FlushMode提供了4种缓存模式:MANUAL、COMMIT、AUTO和ALWAYS。


MANUAL:我们必须在代码中手动调用session.flush(),hibernate才会将脏数据同步到数据库。如果我们忘记了手动刷新,那么就算是通过session.getTransaction().commit()提交了事务,也不能将修改同步到数据库。

Session session = sessionFactory.openSession();
session.setFlushMode(FlushMode.MANUAL);

.....

session.flush();//切记不能遗忘
session.getTransaction().commit();
session.close();


COMMIT:当数据库事务提交的时候会刷新缓存,当然手动调用flush()肯定也是可以的,不过没有必要罢了。

Session session = sessionFactory.openSession();
session.setFlushMode(FlushMode.COMMIT);

.....

session.getTransaction().commit();
session.close();


AUTO:The Session is sometimes flushed before query execution in order to ensure that queries never return stale state. This is the default flush mode. 事务提交或者手动刷新,都能将脏数据同步到数据库。除此之外,某些查询出现的时候也会导致缓存刷新。


ALWAYS:The Session is flushed before every query. This is almost always unnecessary and inefficient. 只要有查询出现,或者事务提交,或者手动刷新,都会导致缓存刷新。这个策略性能比较差,实际中不会使用。


hibernate默认的刷新模式是AUTO,但是这个策略并不合理, 实际中也不会用到。

public void testFifferenceBetweenAutoAndAlways() {
	Session session = sessionFactory.openSession();
	session.setFlushMode(FlushMode.AUTO);
	session.beginTransaction();

	Student student = new Student();
	student.setId(1);
	student.setAge(100000);

	session.update(student);

	System.out.println("after save");

	Query query = session.createQuery("from Student where id =2");
	System.out.println(query.list().get(0));

	System.out.println("after query");

	session.getTransaction().commit();
	session.close();
}


这段代码我们设置的是AUTO,代码中出现了一次查询,按道理来说这次查询并不需要将update操作同步到数据库。但是实际上执行结果如下:



可以看到AUTO并没有我们想象中的那么智能,stackoverflow上这篇文章也列出了AUTO一些不好的地方。

1.you don't control when Hibernate will decide to execute UPDATE/INSERT/DELETE.

2.potential performance issues because every object modification may lead to dirty checking + DML statement execution.

3.you are not taking advantage of batching and other optimizations that Hibernate can perform when it is not trying to avoid 'stale' state


所以最常用其实是MANUAL和COMMIT策略,如果使用了MANUAL,切记需要手动刷新session缓存。


: Request Parameters: {"requestBody":"{\"number\":\"7777776999\",\"xiadan\":\"2025-09-18\",\"jiaohuo\":\"\"}"} 2025-09-18T10:48:58.761+08:00 ERROR 19496 --- [p-nio-80-exec-1] org.hibernate.AssertionFailure : HHH000099: an assertion failure occurred (this may indicate a bug in Hibernate, but is more likely due to unsafe use of the session): org.hibernate.AssertionFailure: null id in com.kucun.entity.Dingdan entry (don't flush the Session after an exception occurs) 2025-09-18T10:48:58.773+08:00 ERROR 19496 --- [p-nio-80-exec-1] c.k.d.validation.UniqueEntityValidator : Error validating entity uniqueness org.hibernate.AssertionFailure: null id in com.kucun.entity.Dingdan entry (don't flush the Session after an exception occurs) at org.hibernate.event.internal.DefaultFlushEntityEventListener.checkId(DefaultFlushEntityEventListener.java:81) ~[hibernate-core-6.2.20.Final.jar:6.2.20.Final] at org.hibernate.event.internal.DefaultFlushEntityEventListener.getValues(DefaultFlushEntityEventListener.java:175) ~[hibernate-core-6.2.20.Final.jar:6.2.20.Final] at org.hibernate.event.internal.DefaultFlushEntityEventListener.onFlushEntity(DefaultFlushEntityEventListener.java:134) ~[hibernate-core-6.2.20.Final.jar:6.2.20.Final] at org.hibernate.event.service.internal.EventListenerGroupImpl.fireEventOnEachListener(EventListenerGroupImpl.java:127) ~[hibernate-core-6.2.20.Final.jar:6.2.20.Final] at org.hibernate.event.internal.AbstractFlushingEventListener.flushEntities(AbstractFlushingEventListener.java:227) ~[hibernate-core-6.2.20.Final.jar:6.2.20.Final] at org.hibernate.event.internal.AbstractFlushingEventListener.flushEverythingToExecutions(AbstractFlushingEventListener.java:90) ~[hibernate-core-6.2.20.Final.jar:6.2.20.Final] at org.hibernate.event.internal.DefaultAutoFlushEventListener.onAutoFlush(DefaultAutoFlushEventListener.java:48) ~[hibernate-core-6.2.20.Final.jar:6.2.20.Final] at org.hibernate.event.service.internal.EventListenerGroupImpl.fireEventOnEachListener(EventListenerGroupImpl.java:127) ~[hibernate-core-6.2.20.Final.jar:6.2.20.Final] at org.hibernate.internal.SessionImpl.autoFlushIfRequired(SessionImpl.java:1375) ~[hibernate-core-6.2.20.Final.jar:6.2.20.Final] at org.hibernate.query.sqm.internal.ConcreteSqmSelectQueryPlan.lambda$new$0(ConcreteSqmSelectQueryPlan.java:108) ~[hibernate-core-6.2.20.Final.jar:6.2.20.Final] at org.hibernate.query.sqm.internal.ConcreteSqmSelectQueryPlan.withCacheableSqmInterpretation(ConcreteSqmSelectQueryPlan.java:303) ~[hibernate-core-6.2.20.Final.jar:6.2.20.Final] at org.hibernate.query.sqm.internal.ConcreteSqmSelectQueryPlan.performList(ConcreteSqmSelectQueryPlan.java:244) ~[hibernate-core-6.2.20.Final.jar:6.2.20.Final]
最新发布
09-19
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值