Reputation: 2465
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?
Upvotes: 16
Views: 17298
Reputation: 5947
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:
select new Movie(m.id, m.story) from Movie m
Upvotes: 0
Reputation: 11752
The one possible solution is:
SELECT movie
FROM Movie movie LEFT JOIN FETCH movie.referencedEntities
WHERE...
Other could be to use @Transactional on method in ManagedBean or Stateless and 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.
Upvotes: 3
Reputation: 176
You can use the fetch all properties
keywords in your query:
SELECT movie
FROM Movie movie FETCH ALL PROPERTIES
WHERE ...
Upvotes: 2
Reputation: 9443
To quote the JPA spec (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.
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
Upvotes: 1
Reputation: 11311
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.
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);
}
}
}
}
Upvotes: -1
Reputation: 597016
I'd try Hibernate.initialize(movie)
. But calling the getter (and adding a comment that this forces initialization) is not that wrong.
Upvotes: 3