force create hibernate envers audit events
Recently, I had decided to adopt Hibernate Envers to audit changes to several tables on my database. I decided to attempt to force create the insert events that would have been generated had I been using Envers from the start. Unfortunately, there wasn’t an obvious way to do this, short of writing my own SQL scripts. That approach is fine, but I had several tables to migrate, and I prefer to avoid bypassing Hibernate when I can.
This is what I came up with:
Envers works by using Ejb3 listeners to find out when an entity is inserted, updated, deleted, etc. So, the obvious solution was to trigger this listener. The listener is an instance of org.hibernate.ejb.event.EJB3PostInsertEventListener and expects to receive PostInsertEvents.
PostInsertEvent pie = new PostInsertEvent(entity, entity.getId(), state, persister, source); eventListener.onPostInsert(pie);
Entity is simply the entity that you saved to the database, so of course we already have that. We just need to figure out what Envers expects for the source, state, and the persister.
The source is simple. This turns out to be the Hibernate session itself, which you can obtain from the JPA EntityManager:
EventSource source = (SessionImpl)jpaEntityManager.getDelegate();
The persister is an instance of an EntityPersister, which just captures the mapping and persistence logic for the given entity. Every entity has one, and this can be obtained from the EventSource.
The state is an object array containing the values of each property that we are persisting. It can be obtained from the EntityPersister.
Object[] state = persister.getPropertyValuesToInsert( entity, null, source );
So, basically, all of that information could be obtained with just the entity and the session. I’m not sure why they make us pass in all these additional fields when you can get them all from just the EventSource and the entity itself, but whatever. Once we have all that information, we assemble the PostInsertEvent and pass it to an instance of the AuditEventListener that will do the work. We don’t even need to hook into the one that Hibernate creates (from your configuration files), we can just create it “by hand”.
The complete code is this:
// instantiate the event listener
AuditEventListener eventListener = new AuditEventListener();
eventListener.initialize(HibConversation.getHibernateConfiguration());
EventSource source = (SessionImpl)jpaEntityManager.getDelegate(); EntityPersister persister = source.getEntityPersister( null, entity );
// create an insertion event for each entity you want to audit
Object[] state = persister.getPropertyValuesToInsert( entity, null, source ); PostInsertEvent pie = new PostInsertEvent(entity, shippingRate.getId(), state, persister, source);
eventListener.onPostInsert(pie);