piątek, 21 października 2011

Immutable value objects with Hibernate

Value objects are one the foundations of the Domain Driven Design. To take most of the benefits connected to them they should be immutable. This however causes some challenges when using them as an attributes of entities mapped using Hibernate.

Let's take a look at sample value object representing currency.

package com.acme;

public final class Currency implements Serializable {

 public static final Currency EUR = new Currency("EUR");  
 public static final Currency USD = new Currency("USD");  

 private final String code;
 
 public static Currency valueOf(String code) {
  if (EUR.getCode().equals(code) {
   return EUR;
  }
  if (USD.getCode().equals(code) {
   return EUR;
  }
  return new Currency(code);
 }

 private Currency(String code) {
  this.code = code;
 }
 
 public String getCode() {
  return code;
 }
 
 @Override
 public boolean equals(Object obj){
  if (!(obj instanceof Currency))
   return false;
  return code.equals(((Currency) obj).getCode()); 
 }
 
 @Override
 public int hashCode(){
  return code.hashCode();
 }
 
 @Override
 public String toString() {
  return code;
 }
}

Currency class is immutable which means that it is final, all of its fields are immutable and declared as final. Moreover it does not have any public constructor but instead it has a factory method which caches most commonly used values.

Now let's assume that we have an Order entity which has Currency as one of its attributes:

package com.acme;

import javax.persistence.Entity;

@Entity
public class Order {
 // ...
 private Currency currency;

 // ...
 public Currency getCurrency() {
  return currency;
 }

 public void setCurrency(Currency currency) {
  this.currency = currency;
 }
}

To map Order.currency attribute we need to create implementation of org.hibernate.usertype.UserType interface which will handle mapping of Currency class to VARCHAR database column.

package com.acme;

import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;

public class CurrencyType extends AbstractImmutableType {
 
 public static final String TYPE = "com.acme.CurrencyType"; 

 private static final int[] SQL_TYPES = { 
    Types.VARCHAR 
 };
    
 public CurrencyType() {
  super();
 }

 public Object nullSafeGet(ResultSet rs, String[] names, 
    Object owner) throws SQLException {
  String value = rs.getString(names[0]);
  if (rs.wasNull()) {
   return null;
  }
  return Currency.valueOf(value);
 }

 public void nullSafeSet(PreparedStatement st, Object value, 
    int index) throws SQLException {
  if (value != null) { 
   st.setString(index, ((Currency)value).getCode());
  } else {
   st.setNull(index, SQL_TYPES[0]);
  }
 }

 public Class<?> returnedClass() {
  return Currency.class;
 }

 public int[] sqlTypes() {
  return SQL_TYPES;
 }
}

It uses AbstractImmutableType class which is defined as follows:

package com.acme;

import java.io.Serializable;

import org.hibernate.usertype.UserType;

public abstract class AbstractImmutableType 
  implements UserType {

 public AbstractImmutableType() {
  super();
 }

 public boolean isMutable() {
  return false;
 }
 
 public Serializable disassemble(Object value) {
  return (Serializable) value;
 }
 
 public Object assemble(Serializable cached, Object owner) {
  return cached;
 }

 public Object deepCopy(Object value) {
  return value;
 }

 public Object replace(Object original, Object target, 
   Object owner) {
  return original;
 }
 
 public boolean equals(Object x, Object y) {
  if (x != null && y != null) {
   return x.equals(y);
  }
  // Two nulls are equal as well
  return x == null && y == null;
 }

 public int hashCode(Object x) {
  if (x != null) {
   return x.hashCode();
  }
  return 0;
 }
}

To use CurrencyType we have to annotate Order.currency attribute with Hibernate's @Type annotation:

package com.acme;

import javax.persistence.Entity;

import org.hibernate.annotations.Type;

@Entity
public class Order {
 // ...
 private Currency currency;

 // ...
 @Type(type = CurrencyType.TYPE)
 public Currency getCurrency() {
  return currency;
 }

 public void setCurrency(Currency currency) {
  this.currency = currency;
 }
}

Mixins aka traits implementation in java

Mixins, also known as traits in some programming languages, are very useful language constructions. They cover most of the use cases where one's might want to use multi-inheritance but conceptually are much simpler. Unfortunately java does not support mixins. Nonetheless with some help from one of bytecode generation libraries like CGLib or javassit it is possible to implement simple mixins support in less then 100 lines of code.

The idea is to use java interface to define mixin methods. Additionally such interface should be marked with @Mixin annotation which will provide information about the class which implements mixin interface and acts as, possibly incomplete, mixin implementation.

@Retention(RetentionPolicy.RUNTIME)
@Target(TYPE)
public @interface Mixin {
  /** 
   * Class implementing mixin interface.
   */
  Class<?> impl();
}

The class which wants to extend mixin must be defined as abstract and must implement mixin interface. Class might extend more then one mixin.

The actual, non abstract implementation for mixin methods is build by helper class which here is called MixinBuilder.

Simple yet complete implementation of MixinBuilder using javassit library is presented below. It does not perform any kind of validation of the setup. Moreover it does not handle the case when more then one mixin defines and implement the same method or two mixins depend on each other. But all those limitations are quite easy extensions to presented code.

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.HashMap;
import java.util.Map;

import javassist.util.proxy.MethodHandler;
import javassist.util.proxy.ProxyFactory;

public class MixinBuilder {
 private static Object invoke(Method method, Object target, 
    Object[] args) throws Exception {
  return target.getClass().getMethod(method.getName(),
    method.getParameterTypes()).invoke(target, args);
 }
 
 @SuppressWarnings("unchecked")
 private static <T> T newMixinInstance(final Object parent, 
    final Class<T> mixinImpl) {
  try {
   final ProxyFactory proxy = new ProxyFactory();
   proxy.setSuperclass(mixinImpl);
   return (T) proxy.create(null, null, new MethodHandler() {
    public Object invoke(Object target, Method method, 
      Method superMethod, Object[] args) 
      throws Throwable {
     // Delegate invocations of abstract methods 
     // to parent object.
     if (Modifier.isAbstract(method.getModifiers())) {
      return MixinBuilder.invoke(method, parent, args);
     }
     return superMethod.invoke(target, args);
    }
   });
  } catch (Exception e) {
   throw new IllegalArgumentException(e);
  }
 }
 
 @SuppressWarnings("unchecked")
 public static <T> T newInstance(final Class<T> mainClass) {
  try {
   final ProxyFactory proxy = new ProxyFactory();
   proxy.setSuperclass(mainClass);
   return (T) proxy.create(null, null, new MethodHandler() {
    private Map<Class<?>,Object> mixinMap = 
      new HashMap<Class<?>, Object>();
    
    public Object invoke(Object target, Method method, 
        Method superMethod, Object[] args) 
        throws Throwable {
     // Delegate invocations of abstract methods 
     // to mixin.
     if (Modifier.isAbstract(method.getModifiers())) {
      Class<?> mixinInterface = method.getDeclaringClass();
      if (!mixinMap.containsKey(mixinInterface)) {
       Mixin annotation = mixinInterface.getAnnotation(
          Mixin.class);
       final Class<?> mixinClass = annotation.impl();
       mixinMap.put(mixinInterface, 
          newMixinInstance(target, mixinClass));
      }
      Object mixin = mixinMap.get(mixinInterface);
      return MixinBuilder.invoke(method, mixin, args);
     }
     return superMethod.invoke(target, args);
    }
   });
  } catch (Exception e) {
   throw new IllegalArgumentException(e);
  }
 }

Sample usage of MixinBuilder might look like this:

@Mixin(impl = MixinAImpl.class)
 public interface MixinA {
  void a();
  void doA();
 }
 
 public abstract class MixinAImpl implements MixinA {
  public void a() {
   System.out.println("MixinAImpl.a()");
   doA();
  }
 }
 
 @Mixin(impl = MixinBImpl.class)
 public interface MixinB {
  void b();
  void doB();
 }
 
 public abstract class MixinBImpl implements MixinB {
  public void b() {
   System.out.println("MixinBImpl.b()");
   doB();
  }
 }
 
 public abstract class Test implements MixinA, MixinB {
  public void doA() {
   System.out.println("Test.doA()");
   b();
  }
  public void doB() {
   System.out.println("Test.doB()");
  }
  public void test() {
   System.out.println("Test.test()");
   a();
  }
 
  public static void main(String[] args) {
   MixinBuilder.newInstance(Test.class).test();
  }
 }

When executed it will display following output:

Test.test()
MixinAImpl.a()
Test.doA()
MixinBImpl.b()
Test.doB()

We can clearly see how execution of single method on Test class causes MixinAImpl execution which indirectly calls Test and MixinBImpl.

If you need more complete ready to use solution check out: Multiple Inheritance and Traits in Java