在Hibernate中在运行时急切加载整个对象图

时间:2013-01-07 12:35:15

标签: java hibernate lazy-loading eager-loading

请在继续说“查询查询中的提取类型”之前阅读。那不是我想要的。

我正在寻找一种方法来急切加载一个完整的对象图(对象+所有子对象及其所有子对象等)。

想要枚举要加载的所有属性。直到运行时我才知道它们。

N + 1个查询不是问题。但是在这个神奇的操作结束时,我不希望在我的图表中留下单个代理或惰性集合。

应该可以编写一些反射和递归查看所有属性的代码。但是收藏会使这种尴尬和复杂。

有些人推荐Dozer用于此类事情,但这似乎有点过分,所以我想将其作为最后的手段保存。

2 个答案:

答案 0 :(得分:0)

一个简单的解决方案是为所有集合(1:N和N:M)和关联(1:1)指定lazy="false"

这会将整个图表加载到每个事务的内存中。因此,要使其正常工作,您最多只能进行一次交易,否则会严重影响性能。

虽然这可以做你想要的,但成本可能太高了。请注意,您可以使用“获取配置文件”在运行时选择不同的策略,但是Hibernate总是会在您请求对象时为您提供一个副本,因此每次都必须复制图形。

对我来说,这听起来像Hibernate只是完成任务的错误工具。使用Hibernate映射实体很舒服,但代价是Hibernate会泄漏到您的模型和业务代码中。如果Hibernate施加的限制不适合您的账单,那么您应该查看其他地方。

也许你可以使用Record类型而不是完全充实的Java bean。如果是,那么您可以查看jOOQframeworks that implement the "active record" pattern

如果您需要bean并且您不限于某种类型的数据库,请尝试使用db4o等OO数据库。

最后,为什么要使用SQL呢?如果您总是需要整个对象图,为什么不简单地将其序列化为文件并在启动时加载它?或者使用memory-resident database

答案 1 :(得分:0)

我曾经需要类似的东西,我不得不使用Reflection来解决它。在我的情况下,我使用hql来检索记录。此外,这是我为急切加载定义为延迟加载的记录创建的方法,因此您可能希望调整第一个方法,以便不查找FetchType.LAZY属性并始终以其方式获取它。

我构建的方法之一将“准备”延迟抓取。基本上有两种方法:在@ManyToOne的hql上使用“left join fetch”,在@OneToMany和@ManyToMany上使用hibernate.initialize()。

因此,第一个方法返回一个字符串,其中包含hql所需的“left john fetch”es,并且还构建了一个nToMany字段列表,这些字段必须在执行查询后由Hibernate.initialize()调用。 / p>

private String buildLazyFetch(Class<? extends GenericEntity> entityClass, List<String> nToManyFields) {
    String lazyFetches = new String();
    lazyFetches += " fetch all properties ";
    // iterate through all fields looking for lazy loaded relationships
    for (Field f : entityClass.getDeclaredFields()) {
        ManyToOne manyToOne = f.getAnnotation(ManyToOne.class);
        if (manyToOne != null) {
            if (manyToOne.fetch().equals(FetchType.LAZY)) {
                lazyFetches += " left join fetch t." + f.getName() + " ";
            }
        }
        OneToMany oneToMany = f.getAnnotation(OneToMany.class);
        if (oneToMany != null) {
            if (oneToMany.fetch().equals(FetchType.LAZY)) {
                nToManyFields.add(f.getName());
            }
        }
        ManyToMany manyToMany = f.getAnnotation(ManyToMany.class);
        if (manyToMany != null) {
            if (manyToMany.fetch().equals(FetchType.LAZY)) {
                nToManyFields.add(f.getName());
            }
        }
    }
    return lazyFetches;
}

并且在执行hql之后,调用:

private void lazyFetchNToMany (List<String> nToManyFields, GenericEntity entity) {
    for (String field : nToManyFields) {
        try {
            Hibernate.initialize(BeanUtils.getProperty(entity, field));
        } catch (HibernateException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
    }
}

我知道这并不完全符合您的预期,但如果您找不到所需的解决方案,它可能会帮助您