ObjectDB ObjectDB

Entity listener - event instead of object

#1

Hi,

Currently I'm playing with entity listener to implement auditing with difference between values in updated entity. There is a problem however with accessing "old" version of object stored in database. I can't find the nice way to use EntityManager in EntityListener. Because of that, I can't store separate entity for given event. Right now I'm using BaseEntity which contains


@ElementCollection
private Set<ActivityLog> activities;

This way I can add events to given object without using EM to persist new object.

What I want to accomplish is to log difference between new and old values for every change in database.

This is example how this can be done with hibernate:


@Override
    public final boolean onPreUpdate(PreUpdateEvent event) {
        try {
            // TODO : need to get the actor ID somehow
            final Long actorId = 0L;
            final Serializable entityId = event.getPersister().hasIdentifierProperty() ? event.getPersister().getIdentifier(event.getEntity(), event.getPersister().guessEntityMode(event.getEntity()))
                    : null;
            final String entityName = event.getEntity().getClass().toString();
            final Date transTime = new Date(); // new Date(event.getSource().getTimestamp());
            final EntityMode entityMode = event.getPersister().guessEntityMode(event.getEntity());
            Object oldPropValue = null;
            Object newPropValue = null;

            // need to have a separate session for audit save
            StatelessSession session = event.getPersister().getFactory().openStatelessSession();
            session.beginTransaction();

            // get the existing entity from session so that we can extract existing property values
            Object existingEntity = session.get(event.getEntity().getClass(), entityId);

            // cycle through property names, extract corresponding property values and insert new entry in audit trail
            for (String propertyName : event.getPersister().getPropertyNames()) {
                newPropValue = event.getPersister().getPropertyValue(event.getEntity(), propertyName, entityMode);
                // because we are performing an insert we only need to be concerned will non-null values
                if (newPropValue != null) {
                    // collections will fire their own events
                    if (!(newPropValue instanceof Collection)) {
                        oldPropValue = event.getPersister().getPropertyValue(existingEntity, propertyName, entityMode);
                        if (LOG.isDebugEnabled()) {
                            LOG.debug("{} for: {}, ID: {}, property: {}, old value: {}, new value: {}, actor: {}, date: {}", new Object[] { OPERATION_TYPE_UPDATE, entityName, entityId, propertyName, oldPropValue, newPropValue, actorId, transTime });
                        }
                        session.insert(new AuditTrail(entityId.toString(), entityName, propertyName, oldPropValue != null ? oldPropValue.toString() : null, newPropValue != null ? newPropValue
                                .toString() : null, OPERATION_TYPE_UPDATE, actorId, transTime));
                    }
                }
            }

            session.getTransaction().commit();
            session.close();

It's only a small part of the code. So, is it somehow possible to implement something similar in ODB? Or is there any way to implement this in current version of ODB?

edit
delete
#2

Support of automatic auditing (i.e. managing and preserving of entity update history) is expected to be added in a future version of ObjectDB. Please subscribe to this feature request (and extend the request if necessary).

Currently this has to be handled by the application. Please be more specific regarding the technical question. Do you need to obtain an EntityManager from a listener class? You may use the JDO getPersistenceManager method and cast the result to an EntityManager.

ObjectDB Support
edit
delete
#3

I know about this feature request and I'm already subscribed. The main problem for me was to get EM inside listener class. Hibernate uses Session instead of Object as method parameter and you can get EM from this session. I will play with getPersistenceManager() method I'll let you know if it's working.

Thanks for help.

edit
delete
#4

So, I did few tries with getPersistenceManager() method and it looks like it works, but not the way I was hoping for.


@PreUpdate
public void update(Object entity) {
  BaseEntity be = (BaseEntity) entity;
  em = (EntityManager) JDOHelper.getPersistenceManager(entity);
  logger.debug("Entity class: {}", entity.getClass().getName());
  if (entity instanceof Inspiration) {
    logger.debug("Audting entity...");
    Inspiration insp = (Inspiration) entity;
    logger.debug("New inspiration entity ready");
    Inspiration be1 = em.find(Inspiration.class, be.getId());
    logger.debug("Old inspiration entity ready");
    logger.debug("BE1: {}, description: {}", be1.getClass().getName(), be1.getDescription());
    logger.debug("BE: {}, description: {}", insp.getClass().getName(), insp.getDescription());
  }
}

So, the problem is, that EntityManager that you get from getPersistenceManager() hold the new version of object that you want to update. If you 'find()' object on this EntityManager you will get exactly the same version as the one stored in 'entity'. It's ok, because it's the same entity manager that you use to update object. So, how to get old version of given entity from database to check for difference in object fields? It should be something like creating entire new EntityManager that has access to database before update is made.

 

 

edit
delete
#5

On @PreUpdate you should have the original entity object before the update (if your entity classes are enhanced).

You may create a copy of that object (maybe by JPA's detach + merge or by JDO's detachCopy). Then you may be able to compare on @PostUpdate the entity object after the update with the cloned detached entity object before the update.

ObjectDB Support
edit
delete

Reply

To post on this website please sign in.