The problem with Hibernate and GWT is that Hibernate needs to return Hibernate proxy classes so it can detect changes made to your objects and detect access to associations that are not loaded. The problem with this mechanism is that when you try to serialize your classes through GWT-RPC it will break throwing LazyInitializationException. So the solutions is to get rid of those proxy classes. One way that I've seen recommended in the GWT group is to create another object and copy the information. This will work but you will have to code a lot to make this work when your object graph is big. The solution that I have found is to clean the proxies using a HibernateCleaner class:
import java.beans.PropertyDescriptor;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.List;
import org.apache.commons.beanutils.PropertyUtils;
import org.hibernate.EntityMode;
import org.hibernate.Hibernate;
import org.hibernate.Session;
import org.hibernate.metadata.ClassMetadata;
import org.hibernate.proxy.HibernateProxyHelper;
import java.io.Serializable;
import java.sql.Blob;
import java.util.*;
import org.apache.log4j.Logger;
import org.hibernate.proxy.HibernateProxy;
public class HibernateCleaner {
private static final Logger logger = Logger.getLogger(HibernateCleaner.class);
public static Object clean(Session session, Object obj) throws Exception {
return (clean(session, obj, new HashMap<Class, Map<Object, Object>>()));
}
private static Object clean(Session session,
Object obj,
Map<Class, Map<Object, Object>> visitedObjects) throws Exception {
Object newObj, value = null, cleanValue = null;
Map.Entry m;
Class clazz;
Object[] array;
Collection collection;
Map map;
PropertyDescriptor[] descriptors;
String property;
ClassMetadata clazzMetaData;
Map<Object, Object> visitedObjectsInClass;
int index, length, hashCode;
if (obj == null)
return (null);
if ((obj instanceof Boolean) || (obj instanceof Number) ||
(obj instanceof Character) || (obj instanceof String) ||
(obj instanceof Blob) || (obj instanceof InputStream))
return (obj);
if (obj instanceof Date)
return (new Date (((Date) obj).getTime()));
if (obj instanceof Calendar)
return (((Calendar) obj).clone());
if (obj instanceof Object[]) {
array = (Object[]) ((Object[]) obj).clone();
length = array.length;
for (index = 0; index < length; index++)
array[index] = clean(session, array[index], visitedObjects);
return (array);
}
if (obj instanceof Object[]) {
array = (Object[]) ((Object[]) obj).clone();
length = array.length;
for (index = 0; index < length; index++)
array[index] = clean(session, array[index], visitedObjects);
return (array);
}
if (obj instanceof Collection) {
collection = createCollection((Collection) obj);
if (Hibernate.isInitialized(obj)) {
for (Object member: (Collection) obj)
collection.add (clean(session, member, visitedObjects));
}
return (collection);
}
if (obj instanceof Map) {
map = createMap((Map) obj);
if (Hibernate.isInitialized(obj)) {
for (Object member: ((Map)obj).entrySet()) {
m = (Map.Entry) member;
clean(session, m.getKey(), visitedObjects);
clean(session, m.getValue(), visitedObjects);
map.put (m.getKey(), m.getValue());
}
}
return (map);
}
if (obj instanceof HibernateProxy) {
clazz = HibernateProxyHelper.getClassWithoutInitializingProxy(obj);
} else {
clazz = obj.getClass();
}
visitedObjectsInClass = visitedObjects.get(clazz);
if (visitedObjectsInClass == null) {
visitedObjectsInClass = new HashMap<Object, Object>();
visitedObjects.put(clazz, visitedObjectsInClass);
} else if (visitedObjectsInClass.containsKey(obj)) {
return visitedObjectsInClass.get(obj);
}
newObj = clazz.newInstance();
visitedObjectsInClass.put(obj, newObj);
if (!Hibernate.isInitialized(obj)) {
if (session != null) {
clazzMetaData = session.getSessionFactory().getClassMetadata(newObj.getClass());
Serializable id = clazzMetaData.getIdentifier(obj, EntityMode.POJO);
clazzMetaData.setIdentifier(newObj, id, EntityMode.POJO);
}
} else {
descriptors = PropertyUtils.getPropertyDescriptors(newObj);
length = descriptors.length;
for (index = 0; index < length; index++) {
property = descriptors[index].getName();
if (!property.equals("class")) {
try {
value = PropertyUtils.getProperty(obj, property);
cleanValue = clean(session, value, visitedObjects);
PropertyUtils.setProperty(newObj, property, cleanValue);
} catch (NoSuchMethodException e) {
} catch (Exception e) {
if (logger.isInfoEnabled())
logger.info("Error Clean: " + obj.getClass() + " - " + property + " - " + value.getClass() + " - " + cleanValue.getClass());
throw(e);
}
}
}
}
return (newObj);
}
private static Collection createCollection(Collection obj) throws Exception {
Collection newObj = null;
if (obj instanceof SortedSet)
newObj = new TreeSet ();
else if (obj instanceof Set)
newObj = new HashSet ();
else
newObj = new ArrayList ();
return (newObj);
}
private static Map createMap(Map obj) throws Exception {
Map newObj = null;
if (obj instanceof SortedMap)
newObj = new TreeMap ();
else
newObj = new HashMap ();
return (newObj);
}
}
I've used this class in many production applications and it works.
Regards,
Néstor Boscán