i am writing one function in hibernate to recursively initialize all properties of object recursively so whole object graph is loaded.
i have two complex scenarios where i need to use this
1) self composite object like category and sub category ...
@Entity
public class Category {
    @Column(nullable = false, length = 50)
    @NotNull
    @Size(max = 50, message = "{50}")
    protected String name;
    @ManyToOne(targetEntity = Category.class, fetch = FetchType.LAZY, optional = true)
    private Category parentCategory;
    }
2) complex object graph which has lot of object to initialize before it can be used.
problem is i can not use eager fetching because i need this whole object graph only in selective cases, and i want to have generalize code so don't need to write HQL queries for objects.
i have written partial some code for this,
public void recursiveInitliaze(Object obj) throws Exception {
    if(!Hibernate.isInitialized(obj))
        Hibernate.initialize(obj);
    PropertyDescriptor[] properties = PropertyUtils.getPropertyDescriptors(obj);
    for (PropertyDescriptor propertyDescriptor : properties) {
        Object origProp = PropertyUtils.getProperty(obj, propertyDescriptor.getName());
        if (origProp != null) {
            this.recursiveInitliaze(origProp);
        }
        if (origProp instanceof Collection && origProp != null) {               
            for (Object item : (Collection) origProp) {
                this.recursiveInitliaze(item);
            }
        }
    }
}
but it has one issue, it ends into stackoverflow for method invocation because of bi-directional relationships. so how to detect there is bidirectional relationship or is there any better way to implement this?
i think fetch profile can also help but still want to try to implement this if possible as configuring fetch profile at current stage of project is difficult.
Full code:
public <T> T recursiveInitliaze(T obj) {
    Set<Object> dejaVu = Collections.newSetFromMap(new IdentityHashMap<Object, Boolean>());
    try {
        recursiveInitliaze(obj, dejaVu);
    } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
        ReflectionUtils.handleReflectionException(e);
    }
    return obj;
}
private void recursiveInitliaze(Object obj, Set<Object> dejaVu) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {
    if (dejaVu.contains(this)) {
        return;
    } else {
        dejaVu.add(this);
        if (!Hibernate.isInitialized(obj)) {
            Hibernate.initialize(obj);
        }
        PropertyDescriptor[] properties = PropertyUtils.getPropertyDescriptors(obj);
        for (PropertyDescriptor propertyDescriptor : properties) {
            Object origProp = PropertyUtils.getProperty(obj, propertyDescriptor.getName());
            if (origProp != null) {
                this.recursiveInitliaze(origProp, dejaVu);
            }
            if (origProp instanceof Collection && origProp != null) {
                for (Object item : (Collection<?>) origProp) {
                    this.recursiveInitliaze(item, dejaVu);
                }
            }
        }
    }
}
Code of ReflectionUtils:
/**
 * Handle the given reflection exception. Should only be called if no
 * checked exception is expected to be thrown by the target method.
 * <p>Throws the underlying RuntimeException or Error in case of an
 * InvocationTargetException with such a root cause. Throws an
 * IllegalStateException with an appropriate message else.
 * @param ex the reflection exception to handle
 */
public static void handleReflectionException(Exception ex) {
    if (ex instanceof NoSuchMethodException) {
        throw new IllegalStateException("Method not found: " + ex.getMessage());
    }
    if (ex instanceof IllegalAccessException) {
        throw new IllegalStateException("Could not access method: " + ex.getMessage());
    }
    if (ex instanceof InvocationTargetException) {
        handleInvocationTargetException((InvocationTargetException) ex);
    }
    if (ex instanceof RuntimeException) {
        throw (RuntimeException) ex;
    }
    throw new UndeclaredThrowableException(ex);
}
you can use Hibernate.initialize() on the object to initialise it. I'm not sure if this cascades. Otherwise you could check instanceof HibernateProxy which gives you an isInitialised property.
Update: since initialize does not cascade you need to traverse the tree like you do already. Use a Hashset to keep track of objects you already processed.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With