Pages

Sunday, April 08, 2007

Using hibernate and reflection to generate queries on any table with eager fetching of the specified attributes representing the foreign key relations

Below is the code attached with appropriate comments:

package reflection;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.hibernate.Criteria;
import org.hibernate.Hibernate;
import org.hibernate.Session;
import org.hibernate.criterion.Expression;

/**
* @author SacrosanctBlood
*
*
* <p>
* This utility class is used to execute an hibernate query on a single entity class, with any number of conditions
* (equals conditions only, but can be extended)on the attributes of the class. It also fetches the Objects given in the
* list which would otherwise be fetched lazily.
* </p>
*/
public class HibernateQueryUtil {
/**
* This method returns the result of the find query on the given input class.
*
* @param session
* The hibernate session in which the query will be executed. This can not be null.
* @param clazz
* The entity class which is mapped to a table in database, on which the query is executed. This can not
* be null.
* @param conditions
* A map of attributeName = attributeValue for the attributes in the class. These represents the
* conditions for the fetch query. This can be null.
* @param fetchList
* A list of attributes in the class representing a foreign relationship that needs to be fetched
* eagerly. This can be null.
* @return List A list of the resultant Objects of the type clazz which have been eagerly initialized according to
* the fetchList. The List can be empty dependent on the query executed.
* @throws IllegalArgumentException
* Thrown when
* <li>Clazz is not valid</li>
* <li>Attributes are not present in clazz</li>
* <li>Attributes in fetchList is not valid.</li>
*/
public static List find(Session session, Class clazz, Map<String, Object> conditions, List<String> fetchList)
throws IllegalArgumentException {
Set<String> attributes = null;
attributes = initializeAttributes(conditions);
String entityName = clazz.getSimpleName();
List result = null;
if (entityName != null) {
Criteria criteria = session.createCriteria(clazz);
if (conditions != null) {
checkIfAttributesExistInClass(clazz, attributes);
addCriterions(criteria, conditions, attributes);
}
result = executeQuery(criteria);
if (fetchList != null && result != null) {
checkIfAttributesExistInClass(clazz, fetchList);
List<String> methodsToInvoke = convertFetchListToMethodToInvokeList(fetchList);
checkIfMethodsExistInClass(clazz, methodsToInvoke);
initializeFetchList(clazz, methodsToInvoke, result);
}
} else {
throw new IllegalArgumentException("Class " + clazz.getName() + " is not valid");
}

return result;
}

/**
* This method eagerly initializes each of the attributes for each Object in the resultList.
*
* @param clazz
* @param methodsToInvoke
* @param resultlist
* @throws IllegalArgumentException
*/
private static void initializeFetchList(Class clazz, List<String> methodsToInvoke, List resultlist)
throws IllegalArgumentException {
// called methods have no argument(They are just getter methods)
Class params[] = {};
Object paramsObj[] = {};
for (Object resultObject : resultlist) {
if (clazz.isInstance(resultObject)) {
for (String aMethod : methodsToInvoke) {
try {
// get the method
Method thisMethod = clazz.getDeclaredMethod(aMethod, params);
// invoke the method on the object and initiliaze in Hibernate
Hibernate.initialize(thisMethod.invoke(resultObject, paramsObj).toString());
} catch (Exception e) {
e.printStackTrace();
throw new IllegalArgumentException(
"Error while invoking method to initiliaze in HibernateQueryUtil:initilizeFetchList");
}
}

} else {
throw new IllegalArgumentException(
"The resultant Objects of the hibernate query are not the same as the Class "
+ clazz.getSimpleName());
}
}

}

/**
* This method checks if the methodToInvoke is present in the given Class.
*
* @param clazz
* @param methodsToInvoke
* @throws IllegalArgumentException
*/
private static void checkIfMethodsExistInClass(Class clazz, Collection<String> methodsToInvoke)
throws IllegalArgumentException {

for (String thisMethod : methodsToInvoke) {

try {
clazz.getDeclaredMethod(thisMethod);
} catch (SecurityException e) {
throw new IllegalArgumentException("Method with name " + thisMethod + " is not accessible");
} catch (NoSuchMethodException e) {
throw new IllegalArgumentException("Method with name " + thisMethod + " does not exist");
}

}

}

/**
* This method converts the fetchList which is just a list of attributes in class, to the corresponding getter
* method on the attribute. This method is later invoked in <i>initializeFetchList</i>.
*
* @param fetchList
* @return List A list containing methods to be invoked.
*/
private static List<String> convertFetchListToMethodToInvokeList(List<String> fetchList) {
List<String> convertedList = new ArrayList<String>();
for (String thisAttribute : fetchList) {
String firstLetterInUpperCase = String.valueOf(thisAttribute.charAt(0)).toUpperCase();
String getterMethodString = "get" + firstLetterInUpperCase + thisAttribute.substring(1);
convertedList.add(getterMethodString);
}
return convertedList;
}

/**
* This method returns a Set of the attributes present in the Class given as an input to <i>find</i> method.
*
* @param attributes
* @return Set All the attributes.
*/
private static Set<String> initializeAttributes(Map<String, Object> attributes) {
Set<String> keys = null;
if (attributes != null)
keys = attributes.keySet();
return keys;
}

/**
*
* @param clazz
* @param attributes
* @throws IllegalArgumentException
*/

private static void checkIfAttributesExistInClass(Class clazz, Collection<String> attributes)
throws IllegalArgumentException {

for (String thisAttribute : attributes) {
try {
clazz.getDeclaredField(thisAttribute);
} catch (NoSuchFieldException e) {
throw new IllegalArgumentException("Column with name " + thisAttribute + " does not exist");
}
}
}

/**
* This method add new criterions for the criteria object.
*
* @param criteria
* @param columns
* @param keys
*/
private static void addCriterions(Criteria criteria, Map<String, Object> columns, Collection<String> keys) {
for (String thisAttribute : keys) {
Object thisAttributValue = columns.get(thisAttribute);
// equals operator by default
criteria.add(Expression.eq(thisAttribute, thisAttributValue));
}
}

/**
*
* @param criteria
* @return List Result of executing the query
*/
private static List executeQuery(Criteria criteria) {
List result = null;
if (criteria != null) {
result = criteria.list();
}
return result;
}
}

Below is a client that uses this utility class:

package client;

import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.hibernate.Hibernate;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;

import entity.data.Data;
import entity.datachildren.DataChildren;

import reflection.HibernateQueryUtil;

public class Client {
public static void main(String[] args) throws Exception{
SessionFactory factory = new Configuration().configure().buildSessionFactory();
Session session = factory.openSession();
//case 1: get all the Data.
List list = HibernateQueryUtil.find(session, Data.class, null,null);
Iterator iter = list.iterator();
while(iter.hasNext()){
Data data = (Data)iter.next();
}
//case 2: get Data with dataId = 3 and dataBy = "ABC"
Map<String, Object> conditions = new HashMap<String, Object>();
map.put("dataId", new Integer(3));
conditions.put("dataBy", "ABC");

// also fetch Data Childrens
List<String> fetchList = new ArrayList<String>();
fetchList.add("dataChildrens");
// list = HibernateQueryUtil.find(session, Data.class, map,null); throws LazyInitializeException
list = HibernateQueryUtil.find(session, Data.class, conditions,fetchList);//initializes so no problem

session.close();
Set ds = null;
iter = list.iterator();
while(iter.hasNext()){
Data data = (Data)iter.next();
System.out.println(data.getDataId() + " " + data.getDataBy());
ds = data.getDataChildrens();
Iterator dIter = ds.iterator();
while(dIter.hasNext()){
System.out.println("LP: " + ((DataChildren)dIter.next()).getDataChildrenName());
}
}



}

}

Hope this was helpful..