Hibernate Interceptors - Why is onFlushDirty called after onSave?

Plan

I use Hibernate to implement createDate and lastUpdate Timestamps for a small project. I use EmptyInterceptor and overload the provided methods based on the proposed solution that I found here . The solution works just fine, if only a small detail. I want to add a column indicating whether the object has already been updated. I know that I can achieve this by simply comparing if there is a difference in the two created and updated timestamps, but I need this field to indicate an update.

I use the onSave method, which is called when a new object is stored to set the isUpdated value to "N", which indicates that there is no update. In the onFlushDirty () method, I set this value to "Y".

Problem

I would use expext when I create and save a new object, that the createDate and lastUpdate fields have the same date, but the isUpdated field has a value of "N" since there was no update. I use session.save () in my code, there is no session.update (), and also no session.saveOrUpdate (). Hibernate logs show that there is actually an update that sets the isUpdated value to "Y".

What could be the source of this update? Where does it work?

Initializing and saving objects

I disabled auto-commit in hibernate.cfg.xml.

<property name="hibernate.connection.autocommit">false</property> 

This is how I create the object:

 ExampleObject ex = new ExampleObject(); ex.setValue("TestStringValue"); this.session = HibernateUtil.getSessionFactory().openSession(); this.session.beginTransaction(); this.session.save(ex); this.session.getTransaction().commit(); this.session.close(); 

Interceptor

 @Override public boolean onSave(Object entity, Serializable id, Object[] state, String[] propertyNames, Type[] types) { if (entity instanceof TimeStamped) { Date insertDate = new Date(); int indexOfCreateDateColumn = ArrayUtils.indexOf(propertyNames, "createdDate"); int indexOfUpdatedDateColumn = ArrayUtils.indexOf(propertyNames, "lastUpdatedDate"); int indexOfWasUpdated = ArrayUtils.indexOf(propertyNames, "wasUpdated"); state[indexOfCreateDateColumn] =insertDate; state[indexOfUpdatedDateColumn] =insertDate; state[indexOfWasUpdated] ='N'; return true; } return false; } 

The second way is to set lastUpdatedDate and set the isUpdated field to "Y".

 @Override public boolean onFlushDirty(Object entity, Serializable id, Object[] currentState, Object[] previousState, String[] propertyNames, Type[] types) { if (entity instanceof TimeStamped) { int indexOfLastUpdate = ArrayUtils.indexOf(propertyNames, "lastUpdatedDate"); int indexOfWasUpdated = ArrayUtils.indexOf(propertyNames, "wasUpdated"); currentState[indexOfLastUpdate] = new Date(); currentState[indexOfWasUpdated] ='Y'; return true; } return false; } 

Hibernateutil

I use this configuration for the session.

 public class HibernateUtil { private static SessionFactory sessionFactory; private static ServiceRegistry serviceRegistry; static { try { Configuration configuration = new Configuration().setInterceptor(new TimeStampInterceptor()); configuration.configure(); serviceRegistry = new StandardServiceRegistryBuilder().applySettings(configuration.getProperties()).build(); sessionFactory = configuration.buildSessionFactory(serviceRegistry); } catch (HibernateException he) { System.err.println("Error creating Session: " + he); throw new ExceptionInInitializerError(he); } } public static SessionFactory getSessionFactory() { return sessionFactory; } } 

Version

I am using Maven and Java 1.7.0_40.

  <!--. Hibernate --> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-core</artifactId> <version>4.3.4.Final</version> </dependency> 
+7
java state hibernate updates interceptor
source share
3 answers

Decision:

Thanks to the prompt suggested by @ vp8106, I could solve this problem. When I set the lastUpdate value to the same date as the creationDate value during record initialization, I simply compare the two dates. If they are identical, then this is firstupdate, where I need to set the wasUpdated update wasUpdated to 'Y' .

Code snippet:

  @Override public boolean onFlushDirty(Object entity, Serializable id, Object[] currentState, Object[] previousState, String[] propertyNames, Type[] types) { /** * Update the lastUpdateDate value and set the wasUpdated flag to 'Y' */ if (entity instanceof TimeStamped) { int indexOfLastUpdate = ArrayUtils.indexOf(propertyNames, "lastUpdatedDate"); int indexOfCreatedDate = ArrayUtils.indexOf(propertyNames, "createdDate"); int indexOfWasUpdated = ArrayUtils.indexOf(propertyNames, "wasUpdated"); Date createdDate = (Date) previousState[indexOfCreatedDate]; Date lastUpdateDate = (Date) currentState[indexOfLastUpdate]; /** * If createdDate equals lastUpdateDate, this is the first update. * Set the updated column to Y */ if (createdDate.equals(lastUpdateDate)) { logger.warning("This is the first update of the record."); currentState[indexOfWasUpdated] = 'Y'; } // set the new date of the update event currentState[indexOfLastUpdate] = new Date(); return true; } return false; } 
+1
source share

The JavaDoc of the onFlushDirty() method has the following statement:

Called when an object is detected during a flash.

Thus, it makes no difference whether the object is dirty due to the call to update() or whether it is dirty due to the call to save() . Thus, the onFlushDirty() method will be called on every permanent object when flushing the session. A session flash can be triggered explicitly by session.flush() or implicitly in some cases when Hibernate needs it (in your case, before the transaction is completed).

In your case, the wasUpdated property wasUpdated always be saved with the value "Y": first, the onSave() method will be called, which when the flushes session starts, the onFlushDirty() method will be called in the same object.

To solve your problem in the onFlushDirty() method, you should check if the entity has been updated. If my memory serves me correctly when an object inserts into a table (saving a new one), the previous state is null. Suggest implementing onFlushDirty() as follows:

 @Override public boolean onFlushDirty(Object entity, Serializable id, Object[] currentState, Object[] previousState, String[] propertyNames, Type[] types) { if (entity instanceof TimeStamped && previousState!=null) { int indexOfLastUpdate = ArrayUtils.indexOf(propertyNames, "lastUpdatedDate"); int indexOfWasUpdated = ArrayUtils.indexOf(propertyNames, "wasUpdated"); currentState[indexOfLastUpdate] = new Date(); currentState[indexOfWasUpdated] ='Y'; return true; } return false; } 
+5
source share

I have implemented a similar thing and it works without problems. The following differences exist:

  • I use no wasUpdated to compare createdDate and lastUpdatedDate .
  • I use lastUpdatedDate to control sleep mode versions (millisecond accuracy is required to work), so I don’t have to set it myself.

 public boolean onSave(Object entity, Serializable id, Object[] state, String[] propertyNames, Type[] types) { if (entity instanceof TimeStamped) { Date insertDate = new Date(); int indexOfCreateDateColumn = ArrayUtils.indexOf(propertyNames, "createdDate"); state[indexOfCreateDateColumn] = insertDate; return true; } return false; } 

If I were an OP, I would ask if I really needed wasUpdated as a field. This is clearly redundant, as it can be calculated at any time, as the OP did in his answer. The read-only property on TimeStamped must execute.

0
source share

All Articles