6 Replies Latest reply on Jun 23, 2009 6:38 PM by jeanluc

    get unchanged entity from DB

      Hey there,


      I'm trying to get the old state of an object from the database before persisting its changes, just to do some audit logging.
      But when I'm invoking the find method to retrieve the data from the DB it returns (of course) a reference to the modified entity.


      Here an example:



      @Entity
      public class Person {
          @Id
          @GeneratedValue
          public Long mId;
      
          public String mName;
      
          ...
      }
      
      @AutoCreate
      @Statefull
      @Name("bMyAction")
      public MyActionImpl implements MyAction {
          @PersistenceContext
          private EntityManager em;
          ...
          public void doSomething() {
              Person lNew = new Person();
              lNew.setName("old");
              em.persist(lNew);
      
              lNew.setName("new");
              Person lOldState = em.find(Person.class, lP.getId());
      
              if(lNew.getName().equals(lOld.getName()) {
                  // bad case
                  // lNew and lOldState have the same reference 
                  // and of course the properties are equal
                  logger.info("Unfortunately I'm equal");
              } else {
                  // good case
                  logger.info("luckily I got the old state and I can compare the changes");
                  
                  // let's commit the changes after comparing the properties
                  em.persist(lNew);
              }
          }
          ...
      }



      I'm ending up in the bad case.


      Maybe one of you can help me which parameter I have to set somewhere to get my non-dirty object parallel to my dirty one.


      Best Regards and thanks in advance
      empi

        • 1. Re: get unchanged entity from DB
          jeanluc

          The snapshot of the original state is maintained internally by Hibernate so it can determine dirty objects. Since you want to have auditing, have you looked at Envers? It may already do what you want. If you cannot use it directly, at least see how Envers does what it does, you may be able to use the same approach.

          • 2. Re: get unchanged entity from DB

            Thanks a lot Jean Luc, that was a really good hint!!


            However, I'm struggling again with the transaction policy. But this time with Envers.


            Hibernate is doing the INSERTs and UPDATEs just fine, but the Revision-Entries are not persisted at the same time.


            So when I try to receive the revision entries for my persisted entity the result is 0, since the RevisionEntity has not yet been persisted.
            I can see hibernate doing these inserts when my SeamTest has run through.


            In the envers documentation they use transactions to persist the entities, but I'm using
            Stateless session Bean (DAO) where in inject my persistence Manager which means that I can't use the .getTransaction()



            @AutoCreate
            @Stateless
            @Name("bGenericDao")
            public class GenericDaoImpl implements GenericDao {
                @PersistenceContext
                private EntityManager em;
            
                ...
                public void save(Object pObj) {
                    // I guess I need some transaction.begin() here, to tell envers to persist its entities too!?
                 em.persist(pObj);
            
                }
                ...
            }



            do you know any property/configuration detail to tell envers to persist the RevisionsEntities immediately or at least something to use Transactions with an injected entity Manager?


            Thanks
            empi


            • 3. Re: get unchanged entity from DB
              admin.admin.email.tld

              I'm not very familiar with envers, but why can't you use CMT (declarative) rather than BMT in your case?

              • 4. Re: get unchanged entity from DB
                jeanluc

                I don't understand why you want to save the previous state again since it is the same thing as what has been loaded, i.e. the same thing as what has been saved previously. Why not simply save the new state only in the history table and calculate the difference only when you want to display it. In the history table, if you look at, say, version 7 of entity foo, it's implied the previous version was 6, found in the same table, there is no need to store it again.

                • 5. Re: get unchanged entity from DB

                  I'm sorry if I explained the problem wrong in my last post.


                  You're absolutely right, that I should display diffs on demand through revisions.


                  Nevertheless I'd like to write a SeamTest wich retrieves a single revision to perform a diff, but the envers revisionEntity is written when the test has already run through.



                  lSomePersistedEntity.setName("NameChanged");
                  
                  bGenericDao.save(lSomePersistedEntity); 
                  
                  AuditReader lAuditReader = AuditReaderFactory.get(getManager());
                  AuditQuery lRevisions =  AuditReader.createQuery()
                                        .forRevisionsOfEntity(lSomePersistedEntity.class, true, false);
                  
                  int lSize = lRevisions.getResultList().size();
                  assert lSize != 0






                  Hibernate log:


                  ...
                  Hibernate: update SomeEntity set ...
                  Hibernate: select SomeEntity_.mId as mId19_, someentity0_.REV as REV19_, ...
                  Hibernate: insert into EntityRevisions (mId, mTimestamp) values (null, ?)
                  Hibernate: call identity()
                  Hibernate: insert into SomeEntity_AUD (REVTYPE, ...
                  ... 









                  • 6. Re: get unchanged entity from DB
                    jeanluc

                    I haven't yet worked with Envers so I don't have a crystal-clear image of when exactly it does its job in the lifecycle of an entity, but from your description, you need to look at 2 things:



                    • the flushing policy (AUTO, COMMIT or, with Hibernate, MANUAL). Changes in an entity's state do not translate into SQL statements until the persistence context is flushed. You can control flushing programatically.




                    • what is flushed is the set of modified entities in the persistence context. The question is whether Envers stores its stuff in the same data container as with the regular entities. In other words, whether a flush() even considers the entities created by Envers.