java 如何使用 JPA 2.0 急切加载惰性字段?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/7252098/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to eagerly load lazy fields with JPA 2.0?
提问by Zhao Yi
I have an entity class that has a lazy field like this:
我有一个实体类,它有一个像这样的惰性字段:
@Entity
public Movie implements Serializable {
...
@Basic(fetch = FetchType.LAZY)
private String story;
...
}
The story field should normally be loaded lazily because it's usually large. However sometimes, I need to load it eagerly, but I don't write something ugly like movie.getStory() to force the loading. For lazy relationship I know a fetch join can force a eager loading, but it doesn't work for lazy field. How do I write a query to eagerly load the story field?
story 字段通常应该被延迟加载,因为它通常很大。然而有时,我需要急切地加载它,但我不会写一些像 movie.getStory() 这样难看的东西来强制加载。对于惰性关系,我知道 fetch join 可以强制进行急切加载,但它不适用于惰性字段。如何编写查询以急切地加载故事字段?
回答by Bozho
I'd try Hibernate.initialize(movie)
. But calling the getter (and adding a comment that this forces initialization) is not that wrong.
我会尝试Hibernate.initialize(movie)
。但是调用 getter(并添加一个强制初始化的注释)并没有错。
回答by Tom Desair
You can use the fetch all properties
keywords in your query:
您可以fetch all properties
在查询中使用关键字:
SELECT movie
FROM Movie movie FETCH ALL PROPERTIES
WHERE ...
回答by Micha? Ziobro
The one possible solution is:
一种可能的解决方案是:
SELECT movie
FROM Movie movie LEFT JOIN FETCH movie.referencedEntities
WHERE...
Other could be to use @Transactionalon method in ManagedBean or Statelessand try to access movie.getReferencedEntities().size() to load it but it will generate N+1 problem i.e. generating additional N queries for each relationship which isn't too efficient in many cases.
其他可能是在 ManagedBean 或Stateless 中的方法上使用@Transactional并尝试访问 movie.getReferencedEntities().size() 来加载它,但它会产生 N+1 问题,即为每个关系生成额外的 N 个查询,这不是太在许多情况下是有效的。
回答by Steve Ebersole
To quote the JPA spec (2.0, 11.1.6):
引用 JPA 规范 (2.0, 11.1.6):
The LAZY strategy is a hint to the persistence provider runtime that data should be fetched lazily when it is first accessed. The implementation is permitted to eagerly fetch data for which the LAZY strategy hint has been specified.
LAZY 策略是对持久性提供程序运行时的一个提示,即在第一次访问数据时应该延迟获取数据。允许实现急切地获取已为其指定 LAZY 策略提示的数据。
Hibernate only supports what you are trying if you use its bytecode enhancement features. There are a few ways to do that. First is to use the build-time enhancement tool. The second is to use (class-)load-time enhancement. In Java EE environments you can enable that on Hibernate JPA using the 'hibernate.ejb.use_class_enhancer' setting (set it to true, false is the default). In Java SE environments, you need to enhance the classes as they are loaded, either on your own or you can leverage org.hibernate.bytecode.spi.InstrumentedClassLoader
如果您使用其字节码增强功能,Hibernate 仅支持您正在尝试的内容。有几种方法可以做到这一点。首先是使用构建时增强工具。第二种是使用(类)加载时增强。在 Java EE 环境中,您可以使用“hibernate.ejb.use_class_enhancer”设置(将其设置为 true,false 是默认值)在 Hibernate JPA 上启用它。在 Java SE 环境中,您需要在加载类时对其进行增强,您可以自己或可以利用 org.hibernate.bytecode.spi.InstrumentedClassLoader
回答by Aleksandr Kravets
If you don't mind having a POJO as a query result you can use constructor query. This will require your object to have constructor with all needed parameters and a query like this:
如果您不介意将 POJO 作为查询结果,您可以使用构造函数 query。这将要求您的对象具有包含所有需要的参数和如下查询的构造函数:
select new Movie(m.id, m.story) from Movie m
回答by 30thh
I would suggest to traverse the objects using Java reflection calling all methods starting with "get" and repeat this for all the gotten object, if it has an @Entity annotation.
我建议使用 Java 反射遍历对象,调用所有以“get”开头的方法,并对所有获取的对象重复此操作,如果它有 @Entity 注释。
Not the most beautiful way, but it must be a robust workaround. Something like that (not tested yet):
不是最漂亮的方式,但它必须是一个强大的解决方法。类似的东西(尚未测试):
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);
}
}
}
}