Web Service : fine control of object marshalling

When returning persisted objects in a web service, the marshaller will browse all the object tree. 2 problems can occur :

  • The object tree is huge and the resulting XML is too large.
  • Some objects have not been fetched from the database and it throws an exception.

In order to avoid that, it is possible to have a control on object marshalling by providing a custom :

  • AccessorFactory returning a custom Accessor.
  • AnnotationReader returning JAXB annotations depending on the context.

Custom AccessorFactory returning a custom Accessor

When instanciating the JAXBContext, enable the @XmlAccessorFactory annotation :

HashMap<String, Object> props = new HashMap<String, Object>();
props.put(JAXBRIContext.XMLACCESSORFACTORY_SUPPORT, true);
JAXBContext jaxbContext = JAXBContext.newInstance(new Class[] { clazz }, props);

Then you can add annotation :

@XmlAccessorFactory(value = CustomAccessorFactory.class)

Implement you CustomAccessorFactory which will return a CustomReflectionAccessor :

public class CustomAccessorFactory implements AccessorFactory {
    @Override
    public Accessor createFieldAccessor(final Class bean, final Field field, final boolean readOnly) throws JAXBException {
        return new CustomReflectionAccessor(field);
    }
    @Override
    public Accessor createPropertyAccessor(final Class bean, final Method getter, final Method setter) throws JAXBException {
        return new CustomReflectionAccessor(getter, setter);
    }
}

In the CustomReflectionAccessor you will implement get() method that way :

    @Override
    public ValueT get(final BeanT bean) throws AccessorException {
        final ValueT valueT = doGet(bean);
        if (!Hibernate.isInitialized(valueT)) {
            return null;
        }
        return valueT;
    }

There are some traps which can be avoided using the whole code :CustomReflectionAccessor

Custom AnnotationReader

It is also possible (and the two techniques can be combined) to provide a custom AnnotationReader which will return the JAXB annotations depending on the context.

HashMap<String, Object> props = new HashMap<String, Object>();
props.put(JAXBRIContext.ANNOTATION_READER, new CustomAnnotationReader());
JAXBContext jaxbContext = JAXBContext.newInstance(new Class[] { clazz }, props);

Your CustomAnnotationReader should implement AbstractInlineAnnotationReaderImpl.

Categories: Web Services Tags: Tags: , ,

Hibernate : get the modified properties and their old values

Imagine your IHM sends you a big POJO object which is already persisted. Are you going to let Hibernate flush the whole state of the entity without even knowing what has been changed ?

If you can do that, you are lucky. In real applications, it is often necessary to trigger some specific treatments if a property has been changed.

Actually, Hibernate does it all the time, so it has all the needed methods (publicly visibles) in order to know exactly the changes made to your pojo.

Here is the code in order to use those methods.

Get EntityPersister from SessionFactory

You must have a method getSessionFactory() which gives you the Hibernate SessionFactory. Then write a method which will return the entityPersister of your Pojo :

protected EntityPersister getEntityPersister() {
    SessionFactoryImplementor sessionFactoryImplementor = (SessionFactoryImplementor) getSessionFactory();
    return sessionFactoryImplementor.getEntityPersister(POJO.class.getName());
}

Get the databaseSnapshot of your object

With the entityPersister and your session casted as SessionImplementor, you can get the databaseSnapshot, which is an array of Objects, the persisted values of the properties of your pojo. You must have a getId() method on your pojo in order to get its primary key value.

EntityPersister entityPersister = getEntityPersister();
SessionImplementor sessionImplementor = (SessionImplementor) getSession();
PersistenceContext persistenceContext = sessionImplementor.getPersistenceContext();
Object[] databaseSnapshot = persistenceContext.getDatabaseSnapshot((Serializable) pojo.getId(), entityPersister);

Get the indexes of modified properties

entityPersister has a method findModified() which gives you an array of the index of the modified properties :

public int[] findPropertiesModified(POJO pojo) {
    EntityPersister entityPersister = getEntityPersister();
    SessionImplementor sessionImplementor = (SessionImplementor) getSession();
    PersistenceContext persistenceContext = sessionImplementor.getPersistenceContext();
    Object[] databaseSnapshot = persistenceContext.getDatabaseSnapshot((Serializable) pojo.getId(), entityPersister);
    Object[] propertyValues = entityPersister.getPropertyValues(pojo, getSession().getEntityMode());
    return entityPersister.findModified(databaseSnapshot, propertyValues, pojo, sessionImplementor);
}

Get the persisted value

The persisted value of the property is databaseSnapshot[index].

Get the name of the modified property

You can retrieve the name of the modified property from its index with the EntityMetamodel of your entity.

EntityMetamodel metamodel = entityPersister.getEntityMetamodel();
String[] properties = metamodel.getPropertyNames();

The name of the property is properties[index].

Categories: Hibernate Tags: Tags: ,