These old forums are deprecated now and set to read-only. We are waiting for you on our new forums!
More modern, Discourse-based and with GitHub/Google/Twitter authentication built-in.

All times are UTC - 5 hours [ DST ]



Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 14 posts ] 
Author Message
 Post subject: PreInsertEvent, PersistEvent, and SaveOrUpdateEvent
PostPosted: Tue Jan 31, 2006 12:54 pm 
Newbie

Joined: Fri Oct 14, 2005 11:43 am
Posts: 18
Hello all

Is anyone able to point me at either a document, forum post, or the relevant Hibernate classes to work out how these three events are related to one another?

I'm hoping to be able to set a "not-null" property on one of these events, but currently the test for nullability is occurring before my code is called.


Regards
Brian


Top
 Profile  
 
 Post subject:
PostPosted: Tue Jan 31, 2006 1:52 pm 
Newbie

Joined: Fri Oct 14, 2005 11:43 am
Posts: 18
Also, on a related subject, what should the onPreInsert method be returning? I thought that I should be returning true if I modify any element of the "state" array while handling the event, and false otherwise.

However, if I do change the state then I'm seeing the following behaviour:

If I return false then the changes that I made to the state are not persisted but I get no errors. This is what I would expect;

If I return true then I get the exception at the bottom of this post a little time later.

Can anyone tell me why returning true from onPreInsert is affecting the generation of the entity's id?


Regards
Brian

-------------------------------------

org.hibernate.AssertionFailure: null identifier
at org.hibernate.engine.EntityKey.<init>(EntityKey.java:33)
at org.hibernate.event.def.AbstractSaveEventListener.performSaveOrReplicate(AbstractSaveEventListener.java:272)
at org.hibernate.event.def.AbstractSaveEventListener.performSave(AbstractSaveEventListener.java:167)
at org.hibernate.event.def.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:101)
at org.hibernate.event.def.DefaultPersistEventListener.entityIsTransient(DefaultPersistEventListener.java:131)
at org.hibernate.event.def.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:87)
at org.hibernate.event.def.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:38)
at org.hibernate.impl.SessionImpl.firePersist(SessionImpl.java:590)
at org.hibernate.impl.SessionImpl.persist(SessionImpl.java:568)
at org.hibernate.impl.SessionImpl.persist(SessionImpl.java:572)


Top
 Profile  
 
 Post subject:
PostPosted: Tue Jan 31, 2006 2:09 pm 
Senior
Senior

Joined: Tue Aug 23, 2005 8:52 am
Posts: 181
If you want the processing to continue after the "pre" events are run, return a false, If you think your code handles everything in the pre-listeners and dont want hibernate to do the actual operation, return true.
The best source to look at will be the javadocs or the Hibernate sources itself. The package is org.hibernate.event.* and for default implementations is org.hibernate.event.def.*

As for your other post as to why the exceptionis thrown, you need to show some code (and mapping). Do you have an assigned "id" value but havent filled in anything for the identifier?


Top
 Profile  
 
 Post subject:
PostPosted: Tue Jan 31, 2006 2:45 pm 
Newbie

Joined: Fri Oct 14, 2005 11:43 am
Posts: 18
Thanks for your swift reply!

I think that explains the exception if I return true here - I guess I'm short-circuiting the allocation of the id. I did look for an example in the def package, but unfortunately there are no default Pre***Listeners (apart from PreLoad, which returns void), and the javadoc doesn't specify what the return value means. No doubt I'm missing something somewhere :(

Can you (or anyone else) think of any reason why when I return false, the change that I made to the state is not persisted?

The code of my onPreInsert method is below. creationTime is defined as a "calendar" in the mapping file. The logged message is being displayed fine:

Code:
   public boolean onPreInsert(PreInsertEvent event)
   {
      boolean result = false;
      if (AuditedEntityState.class.isAssignableFrom(event.getEntity().getClass()))
      {
         String[] names = event.getPersister().getPropertyNames();
         for (int i = 0; i < names.length; ++i)
         {
            if ("creationTime".equals(names[i]))
            {
               event.getState()[i] = Calendar.getInstance(utcZone);
               if (log.isDebugEnabled())
               {
                  log.debug("Setting creation time for entity of class " + event.getEntity().getClass().getSimpleName() + " succeeded.");
               }
               break;
            }
         }
      }
      return false;
   }


All suggestions welcome :)


Regards
Brian


Top
 Profile  
 
 Post subject:
PostPosted: Tue Jan 31, 2006 3:02 pm 
Senior
Senior

Joined: Tue Aug 23, 2005 8:52 am
Posts: 181
Im not sure if there is a direct relationship between event.getState()[index] and event.getPersister().getPropertyNames(). But thast just my guess, those are uncharted waters for me.
In any case, can you try
Code:
event.getPersister().setPropertyValue(event.getEntity(), i, Calendar.getInstance(utcZone), EntityMode.POJO);

instead of
Code:
event.getState()[i] = Calendar.getInstance(utcZone);


Top
 Profile  
 
 Post subject:
PostPosted: Tue Jan 31, 2006 3:31 pm 
Newbie

Joined: Fri Oct 14, 2005 11:43 am
Posts: 18
Thanks again. That worked! Also, even more simply, the following works:

Code:
((AuditedEntityState)event.getEntity()).setCreationTime(
   Calendar.getInstance(utcZone));



I based my previous solution on the thread at http://forum.hibernate.org/viewtopic.php?t=948346&highlight=preinsertevent on which Gavin indicates that there is a correlation between the property names array and state array.


Now, unfortunately, another oddity has occurred. Having successfully caused the creationTime to be set (either with your suggestion or my variant above), I'm now getting both a PreInsertEvent and a PostUpdateEvent on this entity. Comment out the onPreInsert method, and the PostUpdateEvent goes away.

Any thoughts as to why that might be happening, and suggestions as to how to stop it? Since I also process PostUpdateEvents, I'd much prefer not to receive both.


Regards
Brian


Top
 Profile  
 
 Post subject:
PostPosted: Wed Feb 01, 2006 3:49 am 
Newbie

Joined: Fri Oct 14, 2005 11:43 am
Posts: 18
Is anyone able to help with this problem? I want to modify one attribute of an entity immediately before it's stored for the first time.

I thought that doing this in a PreInsertEventListener would be the correct way, but this is resulting in a PostUpdateEvent also being fired for the entity which is causing problems elsewhere.

Should I be doing this in a different way? Ideas welcome.


Regards
Brian


Top
 Profile  
 
 Post subject:
PostPosted: Wed Feb 01, 2006 5:47 am 
Senior
Senior

Joined: Tue Aug 23, 2005 8:52 am
Posts: 181
Can you check if there is any UPDATE calls that are getting fired. I looked into the code and it looks like only PreInsert and PostInsert get called during EntityInsertAction. PostUpdates get called only during EntityUpdate.

Are you by any chance updating any collection or some such during the insert which is making Hibernate think that there is an update that needs to be done. in any case, you should have an UPDATE statement in the logs if PostUpdate is getting called.


Top
 Profile  
 
 Post subject:
PostPosted: Wed Feb 01, 2006 9:02 am 
Newbie

Joined: Fri Oct 14, 2005 11:43 am
Posts: 18
Thanks for sticking with this :)

There are SQL updates occurring for these entities. If I comment out the body of my onPreInsert method then the updates no longer occur.

The sequence of things in the log is:

1. Auditor.onPreInsert: Setting creation time for entity of class Item succeeded.

2. org.hibernate.SQL.log:344] - insert into ITEMS (....

3. ... other things happen....

4. org.hibernate.SQL.log:344] - update ITEMS set ....


The same "things" in step 3 happen whether or not my onPreInsert method is in the loop.

I think it looks like the change that I'm making during onPreInsert is later being interpretted by a flush operation as having made the entity dirty, hence needing an update, even though the change happened immediately before the insert. Is that possible? If so, how can I change the preInsert state without having the entity marked as being dirty?

Again, thanks for your help with this. I appreciate it.


Regards
Brian


Top
 Profile  
 
 Post subject:
PostPosted: Wed Feb 01, 2006 10:43 am 
Senior
Senior

Joined: Tue Aug 23, 2005 8:52 am
Posts: 181
I think I know why its happening but Id have to go back to your original solution. Looking at the Hibernate code, it looks like whats getting inserted is only using the "state" attribute of the PreInsertEvent. So even though you (and I) suggested that we use the
event.getEntity().setCreationTime(...), i dont think its getting inserted during the insert phase. Only the Update that follows after that detects that the "state" which got stored in the Database(through the insert) is not the same as the one in your Object (set through event.getEntity()). And it fires an update.

So to fix this, it looks like we might have to go back to your original solution, but im still not sure if the order of event.getPersister.getPropertyNames is the same as event.getState(). From some browsing of code, it looks like its the order in which you hvae defined the entries in your Mapping file. If you have defined this property as the fourth one, for e.g. , then
event.getState()[4] = .... should work.

But just to be sure, if the above dint work, you might want to print out the values in onPreInsert and see what the order is.


Top
 Profile  
 
 Post subject:
PostPosted: Wed Feb 01, 2006 11:08 am 
Newbie

Joined: Fri Oct 14, 2005 11:43 am
Posts: 18
Quote:
But just to be sure, if the above dint work, you might want to print out the values in onPreInsert and see what the order is.


Unfortunately one null is going to look much like any other :)

On that thread I mentioned earlier, Gavin strongly suggests (to my reading of it anyway) that the order of the names from persister.getPropertyNames and the order of the state array are the same. Certainly no odd casting exceptions occurred when I stuck a Calendar object into the array at that point.

Is there a way to see parameter values of a parameterized query logged from Hibernate? I have a feeling that I've seen that somewhere, but can't find it now.


Regards
Brian


Top
 Profile  
 
 Post subject:
PostPosted: Wed Feb 01, 2006 11:18 am 
Senior
Senior

Joined: Tue Aug 23, 2005 8:52 am
Posts: 181
bremmington wrote:
On that thread I mentioned earlier, Gavin strongly suggests (to my reading of it anyway) that the order of the names from persister.getPropertyNames and the order of the state array are the same. Certainly no odd casting exceptions occurred when I stuck a Calendar object into the array at that point.

Well, thats probably because you are sending something to an Object Array.

bremmington wrote:
Is there a way to see parameter values of a parameterized query logged from Hibernate? I have a feeling that I've seen that somewhere, but can't find it now.


Trace level logging should give you all parameter assignments.


Top
 Profile  
 
 Post subject:
PostPosted: Wed Feb 01, 2006 11:24 am 
Newbie

Joined: Fri Oct 14, 2005 11:43 am
Posts: 18
Quote:
Well, thats probably because you are sending something to an Object Array.


True, but at some point Hibernate would have read it and tried to cast it into something date-like for the query.

Quote:
Trace level logging should give you all parameter assignments.


Thanks. I found the one I was looking for: log4j.category.org.hibernate.type


Having checked the output of that, you're right in saying that the original insert has a null creationTime set, and the subsequent update has the correct time set.

I'll revert to my original solution and see what output appears...


Top
 Profile  
 
 Post subject:
PostPosted: Wed Feb 01, 2006 11:48 am 
Newbie

Joined: Fri Oct 14, 2005 11:43 am
Posts: 18
I think that I've found a solution, but it seems a little odd.

First, I reverted the onPreInsert so it looked like this:

Code:
   public boolean onPreInsert(PreInsertEvent event)
   {
      if (AuditedEntityState.class.isAssignableFrom(event.getEntity().getClass()))
      {
         String[] names = event.getPersister().getPropertyNames();
         for (int i = 0; i < names.length; ++i)
         {
            if ("creationTime".equals(names[i]))
            {
               Calendar time = Calendar.getInstance(utcZone);
               event.getState()[i] = time;
               if (log.isDebugEnabled())
               {
                  log.debug("Setting creation time for entity of class " +
                     event.getEntity().getClass().getSimpleName() + " in state element " + i);
               }
               break;
            }
         }
      }
      return false;
   }



Tracing through the log, it is clear that the reason this seemed to be ignoring the state change was because Hibernate is correctly writing the date on the insert, but is then doing an update with a null date!

So, I've now altered the code to be:

Code:
   public boolean onPreInsert(PreInsertEvent event)
   {
      if (AuditedEntityState.class.isAssignableFrom(event.getEntity().getClass()))
      {
         String[] names = event.getPersister().getPropertyNames();
         for (int i = 0; i < names.length; ++i)
         {
            if ("creationTime".equals(names[i]))
            {
               Calendar time = Calendar.getInstance(utcZone);
               event.getState()[i] = time;
               ((AuditedEntityState)event.getEntity()).setCreationTime(time);
               if (log.isDebugEnabled())
               {
                  log.debug("Setting creation time for entity of class " +
                     event.getEntity().getClass().getSimpleName() + " in state element " + i);
               }
               break;
            }
         }
      }
      return false;
   }


(ie set the calendar both in the state array and the entity)

This works! The database holds the correct value, and only the insert occurred - no update.

I'm sure this can't be the intended way of working, but the main thing is that it works.

Thanks for all your help.


Regards
Brian


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 14 posts ] 

All times are UTC - 5 hours [ DST ]


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
© Copyright 2014, Red Hat Inc. All rights reserved. JBoss and Hibernate are registered trademarks and servicemarks of Red Hat, Inc.