17

我正在休眠中编写一个函数来递归地递归初始化对象的所有属性,以便加载整个对象图。

我有两个复杂的场景需要使用它

1)自复合对象,如类别和子类别......

@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)复杂的对象图,在使用之前有很多对象需要初始化。

问题是我不能使用急切获取,因为我只在选择性情况下需要整个对象图,并且我想要通用代码,因此不需要为对象编写 HQL 查询。

我为此编写了部分代码,

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);
            }
        }
    }
}

但它有一个问题,由于双向关系,它最终进入方法调用的stackoverflow。那么如何检测是否存在双向关系或者有没有更好的方法来实现呢?

我认为 fetch profile 也可以提供帮助,但如果可能的话,仍然想尝试实现这一点,因为在项目的当前阶段配置 fetch profile 很困难。

4

2 回答 2

11

完整代码:

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);
                }
            }
        }
    }
}

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);
}
于 2014-09-01T14:49:08.457 回答
8

您可以Hibernate.initialize()在对象上使用来初始化它。我不确定这是否会级联。否则,您可以检查instanceof HibernateProxy哪个为您提供了 isInitialized 属性。

更新:由于初始化不级联,你需要像你已经做的那样遍历树。使用 Hashset 来跟踪您已经处理过的对象。

于 2012-07-12T08:53:34.450 回答