package com.breeze.hib;
import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.regex.Pattern;
import org.hibernate.Hibernate;
import org.hibernate.proxy.HibernateProxy;
import com.breeze.util.TypeFns;
/**
* Initializes Hibernate proxies and collections prior to serialization.
* This is used to implement the expand operation.
*
* Be sure to set the default_batch_fetch_size property in your Hibernate configuration, e.g.
* <property name="default_batch_fetch_size">32</property>
* so that lazy loading happens in batches, instead of one at a time.
* @author Steve
*
*/
class HibernateExpander {
/**
* Cause initialization of properties on the objects in the collection.
* The properties to initialize are specified in the expandPaths. For example, if
* roots is a collection of Orders, expandPaths might be
* [ "Customer", "OrderDetails/Product" ]
* @param roots - Collection of Hibernate-mapped objects
* @param expandPaths - properties relative to the roots
*/
public static void initializeList(Collection roots, List<String> expandPaths) {
try {
List<String[]> paths = splitPaths(expandPaths);
for (Object root : roots) {
for (String[] path : paths) {
initializeObjectPath(root, path, 0);
}
}
} catch (Exception e) {
throw new RuntimeException("Exception expanding with expandPaths=" + Arrays.asList(expandPaths), e);
}
}
/**
* Recursively forces loading of each Hibernate proxy in the tree that matches an entry in the expandPath.
* @param parent Top-level object containing the properties
* @param expandPath Path of properties to initialize for each type
* @param pathIndex Where we are in the path
* @throws InvocationTargetException
* @throws IllegalArgumentException
* @throws IllegalAccessException
*/
private static void initializeObjectPath(Object parent, String[] expandPath, int pathIndex)
throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
if (parent == null || pathIndex >= expandPath.length)
return;
String propName = expandPath[pathIndex];
Class clazz = parent.getClass();
PropertyDescriptor pd = TypeFns.findPropertyDescriptor(clazz, propName);
Method method = pd.getReadMethod();
Object child = method.invoke(parent, (Object[]) null);
pathIndex++;
if (child instanceof Collection) {
Collection coll = (Collection) child;
Iterator iter = coll.iterator();
while (iter.hasNext()) {
Object current = iter.next();
Hibernate.initialize(current);
if (pathIndex < expandPath.length)
initializeObjectPath(current, expandPath, pathIndex);
}
} else {
Hibernate.initialize(child);
if (child instanceof HibernateProxy) {
child = ((HibernateProxy) child).getHibernateLazyInitializer().getImplementation();
}
if (pathIndex < expandPath.length)
initializeObjectPath(child, expandPath, pathIndex);
}
}
/**
* Split the strings into their path components.
* E.g. "OrderDetails/Product" becomes [ "OrderDetails", "Product" ]
* @param expandPaths
* @return
*/
private static List<String[]> splitPaths(List<String> expandPaths) {
Pattern delims = Pattern.compile("[/.]");
List<String[]> paths = new ArrayList<String[]>();
for (String expandPath : expandPaths) {
// expandPath e.g. "OrderDetails/Product"
String[] propNames = delims.split(expandPath, 10);
paths.add(propNames);
}
return paths;
}
}