我的HibernateInterceptor出了问题。我扩展EmptyInterceptor
并覆盖onFlushDirty
方法。
我使用此方法手动检查脏值(将先前值与当前值进行比较)以编写已更改实体的审计日志。
问题是,在某些特定情况下,在调用onFlushDirty
方法之前,相同的实体会多次传递给postFlush
方法。之前的值和当前值不会因呼叫而发生变化。所以我的审计日志写了几次相同的更改。
如果我的实体的属性发生更改(例如从NEW
更改为IN_PROGRESS
),则此更改会再次传递给onFlushDirty
两次或更多次。
为什么会这样?这是一种奇怪的行为,在我看来并不期望。
拦截器onFlushDirty
的调用是由Hibernate的auto-flush
行为引起的。在会话期间执行查询时,它会刷新会话中的所有脏实体。我希望这对于一个脏实体只做一次,但似乎即使在刷新后该实体仍被标记为“脏”。然后在会话中再次执行另一个查询 - 再次刷新实体。旧值和当前值与第一次刷新时相同。
我尝试用测试用例重现这个问题,但是我无法挑起这样的多次刷新。我希望你在我调试整个Hibernate代码之前,Hibernate专家可以帮助我解决这个问题。
干杯,克里斯
答案 0 :(得分:7)
经过一些调试后,我找到了onFlushDirty
多次调用的原因:
如上所述,我的应用程序中的flushmode设置为AUTO。在事务中执行查询之前,Hibernate会决定是否刷新脏实体。无论这个决定的结果是什么:onFlushDirty
中的方法HibernateInterceptor
被确认为会话中的每个脏实体,即使决定之后不被刷新。
如果类型A的实体在会话中处于脏状态,并且在此会话中执行实体类型B的查询,则Hibernate不会刷新脏实体,因为它不会影响查询正在使用的表,因此不需要冲洗。
如果在会话中执行了多个查询,则可能会发生多次为同一个脏实体调用onFlushDirty方法但它从未真正刷新到数据库并且在提交正在运行的事务之前一直保持脏。
看一下Hibernate类DefaultAutoFlushEventListener
public void onAutoFlush(AutoFlushEvent event) throws HibernateException {
...
if ( flushIsReallyNeeded(event, source) ) {
log.trace("Need to execute flush");
...
}
else {
log.trace("Dont need to execute flush");
...
}
}