5 Replies Latest reply on Nov 6, 2009 4:14 PM by Jean Luc

    How to know which entities are dirty in HibernateSession?

    Matthias Age Newbie

      Hi,


      I'm using Seam/POJOs/JPA on top of Hibernate.
      I would like to know wich entities are dirty before calling entityManager.flush() - i.e. know for which entities Hibernate would issue an update-statement -  and apply some changes on these records (set some values like who changed the record and so on).


      I know that the Hibernate-Session has a method isDirty(), but this is not enough in my case.


      I could register a Hibernate-Interceptor in my persistence.xml-file


               <property name="hibernate.ejb.interceptor" value="path.to.MyInterceptor" />
      
      



      and intercept the method onFlushDirty(...) and save the dirty objects in a map but how can I access this map later on? I don't have access to the interceptor at runtime (so that I could call interceptor.getDirtyEntities()), have I?



      public class MyInterceptor extends EmptyInterceptor {
               private Map<IdentityKey,Object> dirtyEntitiesMap = new HashMap<IdentityKey,Object>();
      
           @Override
           public boolean onFlushDirty(Object entity, Serializable id,
                     Object[] currentState, Object[] previousState,
                     String[] propertyNames, Type[] types) {
      
                 dirtyEntitiesMap.put(new IdentityKey(id, entity.getClass()), entity);
                 
                 return super.onFlushDirty(entity, id, currentState, previousState, propertyNames, types);
           }
      ...
      }
      



      Are there other possibilites to get the dirty-objects from the hibernate-session?


      Thx
      Matthias

        • 1. Re: How to know which entities are dirty in HibernateSession?
          Francisco Jose Peredo Noguez Master

          It depends on your definition of dirty, when I started using ORMs, my definition of dirty was any object that was already registered in the EditingContext (the EOF equivalent of EntitytManager/HibernateSession) but that had information that had not been persisted.
          The I learned that for Hibernate, new objects and deleted objects are not dirty (only modified objects). Since what I wanted was to get a list of the objects that were going to be saved to the database in the next flush/commit so that I can run validation on them (or just notify the user that there were pending changes) I used interceptors and LifeCycle Callbacks to try and catch all new, modified and to-be-deleted objects... but that made me hit an unbreakable wall: Turns out that if an entity has a not nullable property it can not be registered in the EntitytManager... at first I thought that was a limitation in JPA, but then I decided to try JPA/EclipseLink and discovered this was an Hibernate specific bug, sadly, the Hibernate team is not interested in fixing it.


          Oh, and this is a question much more appropriate for the Hibernate Forum... ;-)

          • 2. Re: How to know which entities are dirty in HibernateSession?
            Matthias Age Newbie

            Thanks for your reply.


            In my case I'm only interested in knowing if my entities loaded from the DB (not newly created ones) where changed by the user.
            If I create new ones I already prepopulate certain auditing-attributes so that they are there if the user decides to persist these records.
            In case of an update however I can only set these attributes if I know the entity was changed - not otherwise.


            So maybe the easier way is to clone all the objects after loading them from DB and compare them before calling flush()?


            Matthias


            BTW: I posted the question here because I found some hibernate solutions to this which are not applicable in SEAM, e.g. SessionFactory.buildSession(new MyInterceptor().

            • 3. Re: How to know which entities are dirty in HibernateSession?
              Jean Luc Apprentice

              You don't need to know which entities changes if you only need to update those. You can add your code (populating the user who changed them) in a @PreUpdate method. The entities which are dirty will be modified accordingly, those that are not will not get this method called since there is nothing to update (and Hibernate will detect that). You can do the same for new entities in a @PrePersist method which is probably identical. You can annotate the same method with both.


              If you also need to record which fields have been changed, I'd suggest to look at Envers first.



              • 4. Re: How to know which entities are dirty in HibernateSession?
                Matthias Age Newbie

                Jean Luc wrote on Nov 05, 2009 19:20:


                You don't need to know which entities changes if you only need to update those.  ...


                Yes, you are right. That's a good point.

                But nontheless I still face a problem: in my @PreUpdate method I would like to fetch one of my components via Component.getInstance(...) in order to set the user who changed the record (I don't know of any other solution to get this value).

                I used a project-structure generated by seam-gen, so there is the main-folder and the hot-folder which use different classloaders.

                (a) If I insert a @PreUpdate-method in my entity, the entity (in main-folder) does not see my components (in hot-folder) and I get a compiler-error!

                (b) If I create a listener and annotate the entity with my listener I get a compile error, too.


                Is there a way to get out of this problem?


                Matthias

                • 5. Re: How to know which entities are dirty in HibernateSession?
                  Jean Luc Apprentice

                  IIRC, a component does not have to be declared in hot folder. You can create a session-scoped one in the main folder which will store the identity of the user (obtained via @Observer("org.jboss.seam.security.postAuthenticate")). Then in the @PreUpdate method you will have access to this component.


                  Another way would be through ThreadLocal but the above is simpler.