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!

Advertisement

@Embeddable with hbm.xml

At work we are using hibernate with annotations. So in some places we use the @Embeddable & @Embedded annotation. What do they do? They are both standard annotations, @Embeddable means “this class will not have it’s own table, and it will be embedded in some other’s class table”, and @Embedded means “embed this field to my table”.

That’s all good, but I’ve returned to my old project (klopaj.com), and what do we have there? hbm.xml files. I confess, I’ve first started using hibernate annotations, and then hbm files, so I’m not a hbm guru (nor annotations, but I know a bit more :)). So, to do the same thing with hbm.xml:


<class name="Poi"...>
...
<component name="geoPoint" access="field" class="GeoPoint">
<property name="latitude" type="double" />
<property name="longitude" type="double"  />
...
</component>

Simple as that, now my Poi table has two additional columns “latitude” and “longitude”, but my Poi class has a reference to GeoPoint instance. :)
But still, I like the annotation solutions prettier, but this one is working as planned.

Introducing the “Toink” sessions

I’ve very pleased to announce something that I want to do in a very long time. I’ve heard a million times “this framework is much better than <insert name> framework”, “it’s cool, but to difficult to setup” ,”too many XMLs” etc.

So what will I do? Simple, try to test all the frameworks as much as I can on a very simple application. The mystery application name is “Toink”, so there’s the name “Toink Session”! And yeah, the name doesn’t mean anything, it’s windows default sound when something goes wrong :)

Every framework will use the same backend (core) that has been implemented in Hibernate + Guice combination. And I’ll with test to see how simple it is to setup some framework, use a “legacy” code, and try to use the old POJO objects. Maybe I will do some performance tests as well, because all the frameworks will you the same core.

All the code, examples, bugs and wiki will be @ code.google.com/p/toink. I still don’t know how often will I post here, but as soon as some framework is done, I’ll post it here.

Ready or now, here we go! The first framework will be Apache Wicket!

I would like to hear your thoughts and ideas in the comments :)

Wicket, Hibernate and filters (web.xml)

I guess everybody read about Open Session In View. So, as the jboss guys said there, we need something like SessionRequest filter. No problem with that. I’ve used almost the same code in my application, but I had issues with it.

What happend? Well, sometimes after saving something wicket would block, wait a minute and throw a “After 1 minute the Pagemap null is still locked by:” exception. I’ve read alot about that, but couldn’t find any solution!

Luckly, I found one mailing list, where somebody said that SessionRequest should be defined before WicketFilter (I’ve lost the link, but when I find it, I’ll post it).  Here is how my web.xml looks like now and it worked!

<filter>
 <filter-name>SessionRequestFilter</filter-name>
 <filter-class>com.vuknikolic.SessionRequestFilter</filter-class>
 </filter>
 <filter-mapping>
 <filter-name>SessionRequestFilter</filter-name>
 <url-pattern>/*</url-pattern>
 </filter-mapping>

<filter>
 <filter-name>wicket.patientportal</filter-name>
 <filter-class>org.apache.wicket.protocol.http.WicketFilter</filter-class>
 <init-param>
 <param-name>applicationClassName</param-name>
 <param-value>com.vuknikolic.WicketApplication</param-value>
 </init-param>
 </filter>
 

UPDATE: I need to find some plugin for posting source code…

UPDATE 2: Found one source code highlighter, yay!