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