Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Hibernate Interceptor onFlushDirty called multiple times with same values

I got a Problem with my HibernateInterceptor. I extend the EmptyInterceptor and override the onFlushDirty Method. I use this method to manually check dirty values (compare previous values to current values) to write audit-logs for changed entities. The problem is, that in some specific situations it happens, that the same entity is passed to the onFlushDirty method several times before the postFlush method is called. The previous values and current values do not change from call to call. So my audit-log writes several times the same changes. If a property of my entity changes (e.g. from NEW to IN_PROGRESS) this change is passed two or more times to the onFlushDirty. Why is this happening? This is real odd behavior and not expected in my opinion.

The call of the interceptor onFlushDirty is caused by the auto-flush behavior of Hibernate. When a query is executed during a session it flushes all dirty entities in the session. I expect this to be done just once for a dirty entity, but it seems that the entity is still marked as "dirty" even after the flush. Then another query is executed within the session and again - the entity is flushed again. Old values and current values are the same as at the first flush.

I tried to reproduce this problem with a test-case but i could not manage to provoke such multiple flushes. I hope you Hibernate experts can help me out of this, before i have to debug the whole Hibernate code.

Cheers, Chris

like image 744
theFriedC Avatar asked Apr 08 '14 08:04

theFriedC


1 Answers

After some debugging, i found the reason for the multiple calls of onFlushDirty:

As explained above, the flushmode in my application is set to AUTO. Before a query is executed within a transaction, Hibernate decides whether to flush dirty entities or not. Whatever the outcome of this decision is: the method onFlushDirty in the HibernateInterceptor is called for sure for every dirty entity in the session even if it is decided not to be flushed afterwards.

If an entity of type A is in dirty state in the session and a query for entity type B is executed within this session, Hibernate does not flush the dirty entity because it does not affect the table the query is using and so the flush is not needed.

If there are multiple queries executed within a session it can occur that the onFlushDirty method is called several times for the same dirty entity but it gets never really flushed to the database and remains dirty until the running transaction is committed.

Have a look at the Hibernate class 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");
...
    }
}
like image 176
theFriedC Avatar answered Nov 01 '22 08:11

theFriedC