Version 2

    Problem:

    You are building a three tier distributed system and you are passing persistent objects between tiers. When you send the objects back to the server, you want to save the only the changes.

    Solutions:

    1)Hibernate added the select-before-update option in 2.1. Setting this option on your class will cause Hibernate to load the data from the database and compare that to the data in your object to determine what has changed. It makes use of a version or timestamp field to enforce optimistic concurrency. This works well but introduces additional database access that could effect performance.

    2)Save the original data for each persistent object and implement a custom EntityPersister to return the original state to Hibernate instead of accessing the state in the database.

    Implementing Solution 2:

    Step 1:  Save the original state for each persistent object

    The easiest way to implement this is to have each persistent object remember the original values of any properties that changed. If you make all your persistent objects proper JavaBeans, you can capture this in the base class when the object fires a propertyChanged event:

    public abstract class AbstractDomainObject implements Serializable {
    
      private PropertyChangeSupport support = new PropertyChangeSupport(this);
    
      private Map originalValues = new HashMap();
    
      public AbstractDomainObject() {
        super();
      }
    
      public Map getOriginalState() {
        return originalValues;
      }
    
      public void addPropertyChangeListener(PropertyChangeListener propertyChangeListener) {
        support.addPropertyChangeListener(propertyChangeListener);
      }
    
      protected void firePropertyChange(String property, Object oldValue, Object newValue) {
        if (!originalValues.containsKey(property)) {
          originalValues.put(property, oldValue);
        }
        support.firePropertyChange(property, oldValue, newValue);
      }
    
    }
    

    Step 2: Create your domain objects

    A domain object might look like follows. Because it fires propertyChange events, the base class will remember original values of properties that changed. Note that you will want to map the properties as access=field since the setter fires a propertyChange event when called.

    public class Customer extends AbstractDomainObject {
    
      private String name;
    
      public String getName() {
        return name;
      }
    
      public void setName(String name) {
        String oldValue = this.name;
        this.name = name;
        firePropertyChange("name", oldValue, name);
      }
    
    }
    

    Step 3: Create a custome entity persister

    Create a custom entity persister and override getCurrentPersistentState. Set the persister for each class to this custom persister.

    public Object[] getCurrentPersistentState(Serializable id, Object version, SessionImplementor session) throws HibernateException {
      AbstractDomainObject entity = (AbstractDomainObject) session.getEntity(new Key(id, this));
      Map originalValues = entity.getOriginalState();
      Type[] types = getPropertyTypes();
      String[] propertyNames = getPropertyNames();
      Object[] values = new Object[propertyNames.length];
      boolean[] includeProperty = getPropertyUpdateability();
      for (int i = 0; i < propertyNames.length; i++) {
        if (includeProperty[i]) {
          if (originalValues.containsKey(propertyNames[i])) {
            values[i] = types[i].disassemble(originalValues.get(propertyNames[i]), session);
          } else {
            values[i] = types[i].disassemble(getPropertyValue(entity, propertyNames[i]), session);
          }
        }
      }
      return values;
    }
    

     

    What about collections you ask?  Well, Hibernate keeps track of changes to collections in the collection class wrappers for you.

    Please note this solution has worked great in my example programs, but I have not yet implemented it in a production program.