Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Hibernate recursive initialization

Tags:

hibernate

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.

like image 531
Jigar Parekh Avatar asked Jul 12 '12 04:07

Jigar Parekh


2 Answers

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);
}
like image 157
alexsmail Avatar answered Oct 12 '22 08:10

alexsmail


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.

like image 39
Firo Avatar answered Oct 12 '22 07:10

Firo