Is there any generally accepted, proven-to-work way using hibernate, that keeps a history of an entitys changes in the database?
We need to keep track of quite some
JBoss envers is perhaps what you are looking for.
The Envers project aims to enable easy auditing/versioning of persistent classes. All that you have to do is annotate your persistent class or some of its properties, that you want to audit, with @Audited. For each audited entity, a table will be created, which will hold the history of changes made to the entity. You can then retrieve and query historical data without much effort.
Similarly to Subversion, the library has a concept of revisions. Basically, one transaction is one revision (unless the transaction didn't modify any audited entities). As the revisions are global, having a revision number, you can query for various entities at that revision, retrieving a (partial) view of the database at that revision. You can find a revision number having a date, and the other way round, you can get the date at which a revision was commited.
Hibernate Envers from JBoss is probably what you are looking for:
The Envers project aims to enable easy auditing/versioning of persistent classes. All that you have to do is annotate your persistent class or some of its properties, that you want to audit, with @Audited. For each audited entity, a table will be created, which will hold the history of changes made to the entity. You can then retrieve and query historical data without much effort.
There is an automatic versioning and auditing project developed by JBoss: envers. I can recommend using this, I've implemented auditing by hand before and that can get really messy when you start approaching more complex usecases.
I've done this with an Interceptor (creating my own based on EmptyInterceptor). I was able to get all changes (merge, save, saveOrUpdate and delete) in onFlushDirty().
The methods of EmptyInterceptor I used:
@Override
public boolean onFlushDirty(Object object, Serializable id,
Object[] newValues, Object[] oldValues, String[] properties,
Type[] types) throws CallbackException {
@Override
public boolean onSave(Object object, Serializable id, Object[] newValues,
String[] properties, Type[] types) throws CallbackException {
@Override
public void onDelete(Object object, Serializable id, Object[] newValues,
String[] properties, Type[] types) throws CallbackException {
In onFlushDirty() I had to query the previous state of the entity:
Connection c = sessionFactory.getCurrentSession().connection();
Session session = sessionFactory.openSession(c);
BaseEntity newBaseEntity = (BaseEntity) object;
BaseEntity oldBaseEntity = (BaseEntity) session.get(newBaseEntity.getClass(), newBaseEntity.getId());
I've had exactly the same problem. One way round it is to lock()
with the previous state and then merge()
with the new state, rather than doing update()
with just the new state.
Then hibernate is aware of the before/after and it is available in the interceptors/event listeners.
HTH.