Hibernate Audit logging – the easy way!

Let’s say you are working on an application where users are constantly adding/changing  new content. You will most likely have to keep a record what’s going on. For example you want to make a wiki-like software, it is crucial to keep a log who added/updated/deleted wiki entries.

If you need that functionality, and you are using Hibernate, then you are one happy coder! Enter ENVERS! As the good people at jboss site say: Easy Entity Auditing.

Here is what you need to do make your entities auditable:

  1. Download latest hibernate with envers bundled (I’m using 3.6.0.Final) and add it to your classpath
  2. Add envers event listeners to your session factory (I’m using spring in my project, so here is a part of my spring.xml):
    <bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
       <!-- Envers - Auditlogging -->
       <property name="eventListeners">
          <map>
             <entry key="post-insert"><ref local="enversListener" /></entry>
             <entry key="post-update"><ref local="enversListener" /></entry>
             <entry key="post-delete"><ref local="enversListener" /></entry>
             <entry key="pre-collection-update"><ref local="enversListener" /></entry>
             <entry key="pre-collection-remove"><ref local="enversListener" /></entry>
             <entry key="post-collection-recreate"><ref local="enversListener" /></entry>
          </map>
       </property>
    </bean>
    <bean id="enversListener" class="org.hibernate.envers.event.AuditEventListener" />
    
  3. Annotate your class with @Audited. If you don’t want to keep track of all properties, just annotate the fields you need with the same @Audited.

And that is it :)

After completing all the three steps you will see new table called revinfo (that keeps all the revisions information, you can change it’s name, prefix/suffix, check out the docs) and <entity>_aud (log for the specified entity).

I had a problem that <entity>_aud didn’t have all the required fields, even with hibernate.hbm2ddl.auto set to update. Adam Warski (great guy that create Envers, thanks man!)  gave somebody a great tip in jboss forum, that helped me. I’ve just added @Audited for my getters and everything worked smoothly.

One thing I needed more is to keep track WHO made the changes. Here is a nice tutorial how to do that. To keep it short:

  1. Extend DefaultRevisionEntity and annotate it with @RevisionEntity
    @RevisionEntity(PectopahRevisionListener.class)
    public class PectopahRevisionEntity extends DefaultRevisionEntity {
    	private String username;
    
    	public String getUsername() { return username; }
    	public void setUsername(String username) { this.username = username; }
    }
    
  2. Create your own RevisionListener
    public class PectopahRevisionListener implements RevisionListener {
        @Autowired
        private UserService userService;
        @Override
        public void newRevision(Object revisionEntity) {
               PectopahRevisionEntity pectopahRevisionEntity = (PectopahRevisionEntity) revisionEntity;
               User user =  userDetails.getCurrentUser();
               pectopahRevisionEntity.setUsername(user.getUsername());
            }
        }
    }
    
  3. In case you are using hbm.xml files (like me), add the following:
    <hibernate-mapping package="...your package...">
    	<class name="PectopahRevisionEntity" table="revinfo">
            <id name="id" type="int">
    			<column name="rev" not-null="true"/>
    			<generator class="native"/>
    		</id>
            <property name="timestamp" type="long" column="timestamp" />
    		<property name="username" type="string" not-null="true"/>
    	</class>
    </hibernate-mapping>

    (here you could rename table’s name and/or fields).

Simple as that. There is also a simple mechanism how to retrieve info from audit log, but I’ll maybe cover that some other time, it’s almost 1am :)

It makes me really happy to find great tools like this one. Easy to setup, clean and simple API, and yeah it does the job!

Advertisements

4 comments

    • Hi,
      here is my part of the code that retrieves data from audit reader (I’ve removed some unrelated fragments)

       // needed imports from envers
      import org.hibernate.envers.AuditReader;
      import org.hibernate.envers.AuditReaderFactory;
      
      // code sample
      AuditReader auditReader = AuditReaderFactory.get(rejoiceDao.getSession());
      List revisionNumbers = auditReader.getRevisions(Poi.class, id);
      revisionUsernames = new ArrayList();
      
      for (Number number : revisionNumbers) {
           Poi revision = auditReader.find(Poi.class, id, number);
                    
           PectopahRevisionEntity pre = auditReader.findRevision(PectopahRevisionEntity.class, number);
           revisionUsernames.add(pre.getUsername());
       }
      

      Just to make it clear rejoiceDao.getSession() returns hibernate session. And in the post you have a definition of PectopahRevisionEntity. Poi is the classes that was annotated as @Audit. I think that should do the trick :)

  1. I am not certain where you are getting your info, but good topic.

    I needs to spend a while learning much more or working
    out more. Thank you for wonderful information I was in
    search of this information for my mission.


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s