This page explains how to load and store instances that are wrapped with a JDK dynamic proxy.
First, you need an interface:
public interface FooInterface {
public Long getId();
public void setId(Long id);
public String getBar();
public void setBar(String bar);
}
Next, a concrete class that implements this interface:
public class Foo implements FooInterface {
private Long id;
private String bar;
public Foo() {}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getBar() {
return bar;
}
public void setBar(String bar) {
this.bar = bar;
}
}
A proxy is a class that wraps a concrete instance that implements a given interface. This is a possible proxy:
public class FooProxy implements InvocationHandler {
Object obj;
public FooProxy(Object obj) {
this.obj = obj;
}
public Object invoke(Object proxy, Method m, Object[] args) throws
Throwable {
try {
// Do something on real object
return m.invoke(obj, args);
} catch (InvocationTargetException e) {
throw e.getTargetException();
} catch (Exception e) {
throw e;
}
}
public static Object newInstance(Object obj, Class[] interfaces) {
return Proxy.newProxyInstance(obj.getClass().getClassLoader(),
interfaces,
new FooProxy(obj));
}
}
Note that this proxy implementation does nothing, in other words, it does not implement any additional functionality. Every method called on the proxy is directly passed on to a method call on the real object. I also included a factory method.
Let's map this with Hibernate XML. You map the interface:
<hibernate-mapping> <class name="FooInterface" table="FOO"> <id name="id" column="FOO_ID" type="long"> <generator class="native"/> </id> <property name="bar" column="BAR"/> </class> </hibernate-mapping>
Hibernate will use the getter and setter methods defined by that interface as property accessors. Finally, you need a Hibernate Interceptor to do the proxy magic when loading and storing an object:
public class FooProxyInterceptor extends EmptyInterceptor {
public String getEntityName(Object object) {
if (Proxy.isProxyClass(object.getClass()) && object instanceof FooInterface)
return FooInterface.class.getName();
return super.getEntityName(object);
}
public Object instantiate(String entityName, EntityMode entityMode, Serializable id) {
if (entityName.equals(FooInterface.class.getName())) {
Foo newFoo = new Foo();
newFoo.setId((Long)id);
FooInterface foo = (FooInterface)
FooProxy.newInstance(newFoo, new Class[]{ FooInterface.class });
return foo;
}
return super.instantiate(entityName, entityMode, id);
}
}
When saving an object, getEntityName() is called. If we would use the default in Hibernate, it would look for a mapping of FooProxy - we don't have that. If a proxied object is saved we instead return the name of the mapped interface.
When loading an object, a concrete instance of Foo is needed, and it has to be wrapped in a proxy.
Comments