Monday, December 1, 2008

Hibernate Lazy Entities Cleaner

For those who have problems with lazy loaded entities, you can use the following (it is tested and used by many)

The cleaning method will traverse all objects in the hierarchy and remove lazy entities until it finds an object of instance BaseBean. For better performance let all your beans implement a dummy interface called BaseBean.

--------------------------------------------------------------------------------------------------

/*
* Copyright 2008, Maher Kilani
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.log4j.Logger;
import org.hibernate.proxy.HibernateProxy;


/**
* This class is implemented to solve the hibernate lazy loading problem with
* flex which doesnt support lazy loading in the meantime.
*
* @author Maher Kilani
*
*/
public class HibernateProxyCleaner
{

private static Logger log = Logger.getLogger(HibernateProxyCleaner.class);

/**
* This function would take as a prameter any kind of object and recursively
* access all of its member and clean it from any uninitialized variables.
* The function will stop the recursion if the member variable is not of type
* baseBean (defined in the application) and if not of type collection
*
* @param listObj
* @throws ClassNotFoundException
* @throws IllegalAccessException
* @throws IllegalArgumentException
* @throws InvocationTargetException
* @throws InstantiationException
*/
@SuppressWarnings("unchecked")
public static void cleanObject(Object listObj, HashSet visitedBeansSet) throws IllegalArgumentException, IllegalAccessException,
ClassNotFoundException, InstantiationException, InvocationTargetException
{
if(visitedBeansSet == null) visitedBeansSet = new HashSet();
if(listObj == null) return;

// to handle the case of abnormal return consisting of array Object
// case if hybrid bean
if(listObj instanceof Object[])
{
Object[] objArray = (Object[]) listObj;
for(int z = 0; z < objArray.length; z++)
{
cleanObject(objArray[z], visitedBeansSet);
}
}
else
{

Iterator itOn = null;

if(listObj instanceof List)
{
itOn = ((List) listObj).iterator();
}
else
if(listObj instanceof Set)
{
itOn = ((Set) listObj).iterator();
}
else
if(listObj instanceof Map)
{
itOn = ((Map) listObj).values().iterator();
}

if(itOn != null)
{
while(itOn.hasNext())
{
cleanObject(itOn.next(), visitedBeansSet);
}
}
else
{
if(!visitedBeansSet.contains(listObj))
{
visitedBeansSet.add(listObj);
processBean(listObj, visitedBeansSet);
}

}
}
}

/**
* Remove the un-initialized proxies from the given object
*
* @param objBean
* @throws Exception
* @throws IllegalAccessException
* @throws ClassNotFoundException
* @throws IllegalArgumentException
* @throws InvocationTargetException
* @throws InstantiationException
*/
@SuppressWarnings("unchecked")
private static void processBean(Object objBean, HashSet visitedBeans) throws IllegalAccessException, IllegalArgumentException,
ClassNotFoundException, InstantiationException, InvocationTargetException
{
Class tmpClass = objBean.getClass();
Field[] classFields = null;
while(tmpClass != null && tmpClass != BaseBean.class && tmpClass != Object.class)
{
classFields = tmpClass.getDeclaredFields();
cleanFields(objBean, classFields, visitedBeans);
tmpClass = tmpClass.getSuperclass();
}
}

@SuppressWarnings("unchecked")
private static void cleanFields(Object objBean, Field[] classFields, HashSet visitedBeans) throws ClassNotFoundException,
IllegalArgumentException, IllegalAccessException, InstantiationException, InvocationTargetException
{
boolean accessModifierFlag = false;
for(int z = 0; z < classFields.length; z++)
{
Field field = classFields[z];
accessModifierFlag = false;
if(!field.isAccessible())
{
field.setAccessible(true);
accessModifierFlag = true;
}

Object fieldValue = field.get(objBean);

if(fieldValue instanceof HibernateProxy)
{
String className = ((HibernateProxy) fieldValue).getHibernateLazyInitializer().getEntityName();
Class clazz = Class.forName(className);
Class[] constArgs = {Integer.class };
Constructor construct = null;
BaseBean baseBean = null;

try
{
construct = clazz.getConstructor(constArgs);
}
catch(NoSuchMethodException e)
{
log.info("No such method for base bean " + className);
}

if(construct != null)
{
baseBean = (BaseBean) construct.newInstance((Integer) ((HibernateProxy) fieldValue).getHibernateLazyInitializer().getIdentifier());
}
field.set(objBean, baseBean);
}
else
{
if(fieldValue instanceof org.hibernate.collection.PersistentCollection)
{
// checking if it is a set, list, or bag (simply if it is a
// collection)
if(!((org.hibernate.collection.PersistentCollection) fieldValue).wasInitialized())
field.set(objBean, null);
else
{
cleanObject((fieldValue), visitedBeans);
}

}
else
{
if(fieldValue instanceof BaseBean || fieldValue instanceof Collection) cleanObject(fieldValue, visitedBeans);
}
}
if(accessModifierFlag) field.setAccessible(false);
}
}
}


--------------------------------------------------------------------------------------------------