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