如何用JPA 2.0急切加载懒字段?

时间:2011-08-31 02:59:56

标签: java hibernate java-ee jpa jpql

我有一个实体类,它有一个像这样的惰性字段:

@Entity
public Movie implements Serializable {
    ...
    @Basic(fetch = FetchType.LAZY)
    private String story;
    ...
}

故事场通常应该懒散地加载,因为它通常很大。但有时候,我需要急切地加载它,但是我不会像movie.getStory()那样写一些丑陋的东西来强制加载。对于懒惰的关系,我知道获取连接可以强制加载,但它不适用于惰性字段。如何编写查询以急切加载故事字段?

6 个答案:

答案 0 :(得分:3)

我试试Hibernate.initialize(movie)。但是调用getter(并添加一个强制初始化的注释)并没有错。

答案 1 :(得分:2)

您可以在查询中使用fetch all properties个关键字:

SELECT movie 
FROM Movie movie FETCH ALL PROPERTIES
WHERE ...

答案 2 :(得分:1)

引用JPA规范(2.0,11.1.6):

  

LAZY策略提示应该获取数据的持久性提供程序运行时   第一次访问时懒洋洋地说。允许实现急切地获取数据   已经指定了LAZY策略提示。

如果使用字节码增强功能,Hibernate仅支持您正在尝试的内容。有几种方法可以做到这一点。首先是使用构建时增强工具。第二种是使用(类)加载时间增强。在Java EE环境中,您可以使用'hibernate.ejb.use_class_enhancer'设置在Hibernate JPA上启用它(将其设置为true,默认为false)。在Java SE环境中,您需要在加载类时自行增强类,或者您可以利用org.hibernate.bytecode.spi.InstrumentedClassLoader

答案 3 :(得分:1)

一种可能的解决方案是:

SELECT movie 
FROM Movie movie LEFT JOIN FETCH movie.referencedEntities
WHERE...

其他可能是在ManagedBean中使用 @Transactional 无状态并尝试访问movie.getReferencedEntities()。size()来加载它但它会生成N + 1问题,即为每个关系生成额外的N个查询,这在许多情况下效率不高。

答案 4 :(得分:0)

我建议使用Java反射遍历对象,调用以“get”开头的所有方法,如果它有@Entity注释,则对所有获取的对象重复此操作。

不是最美丽的方式,但它必须是一个强大的解决方法。类似的东西(尚未测试):

public static <T> void deepDetach(EntityManager emanager, T entity) {
    IdentityHashMap<Object, Object> detached = new IdentityHashMap<Object, Object>();
    try {
        deepDetach(emanager, entity, detached);
    } catch (IllegalAccessException e) {
        throw new RuntimeException("Error deep detaching entity [" + entity + "].", e);
    } catch (InvocationTargetException e) {
        throw new RuntimeException("Error deep detaching entity [" + entity + "].", e);
    }
}


private static <T> void deepDetach(EntityManager emanager, T entity, IdentityHashMap<Object, Object> detached) throws IllegalAccessException, InvocationTargetException {
    if (entity == null || detached.containsKey(entity)) {
        return;
    }

    Class<?> clazz = entity.getClass();

    Entity entityAnnotation  = clazz.getAnnotation(Entity.class);
    if (entityAnnotation == null) {
        return; // Not an entity. No need to detach.
    }

    emanager.detach(entity);
    detached.put(entity, null); // value doesn't matter. Using a map, because there is no IdentitySet.

    Method[] methods = clazz.getMethods();

    for (Method m : methods) {
        String name = m.getName();
        if (m.getParameterTypes().length == 0) {
            if (name.length() > 3 && name.startsWith("get") && Character.isUpperCase(name.charAt(3))) {
                Object res = m.invoke(entity, new Object[0]);
                deepDetach(emanager, res, detached);
            }
            // It is actually not needed for searching for lazy instances, but it will load
            // this instance, if it was represented by a proxy
            if (name.length() > 2 && name.startsWith("is") && Character.isUpperCase(name.charAt(2))) {
                Object res = m.invoke(entity, new Object[0]);
                deepDetach(emanager, res, detached);
            }
        }
    }
}

答案 5 :(得分:0)

如果您不介意将POJO作为查询结果,则可以使用构造函数查询。这将要求您的对象具有包含所有必需参数的构造函数以及如下所示的查询:

select new Movie(m.id, m.story) from Movie m