I have a problem with EAGERs relationships in a big application. Some entities in this application have EAGER
associations with other entities. This become "poison" in some functionalities.
Now my team needs to optimize this functionalities, but we cannot change the fetch type to LAZY, because we would need to refactor the whole application.
So, my question: Is there a way to do a specific query ignoring the EAGERs associations in my returned entity?
Example: when a I have this entity Person, I would like to not bring the address list when I do a query to find a Person.
@Entity
public class Person {
@Column
private String name;
@OneToMany(fetch=FetchType.EAGER)
private List<String> address;
}
Query query = EntityManager.createQuery("FROM Person person");
//list of person without the address list! But how???
List<Person> resultList = query.getResultList();
Thanks!
Updated
The only way I found is not returning the entity, returning only some fields of the entity. But I would like to find a solution that I can return the entity (in my example, the Person
entity).
I'm thinking if is possible to map the same table twice in Hibernate. In this way, I can mapping the same table without the EAGER associations. This will help me in a few cases...
Never actually tried this but might worth a shot... Assuming that the session factory is available at the DAO layer via injection or any other means you can implement something similar in a (probably new) DAO method:
By default, Hibernate's HQL, Criteria and NativeSQL gives us the flexibility to EAGERly load a collection if it is mapped as LAZY in the domain model.
Regarding the other way round, ie., mapping the collection as as EAGER in the domain model and try to do LAZY load using HQL, Criteria or NativeSQL, I couldn't find a straight forward or simpler way in which we can meet this with HQL/Criteria/NativeSQL.
Although we have
FetchMode.LAZY
that we can set on Criteria,it is deprecated and it is equivalent toFetchMode.SELECT
. And effectively FetchMode.LAZY actually results in firing an extra SELECT query and still eagerly loads the collection.But, if we want to LAZY load a collection that is mapped as EAGER, you can try this solution: Make the HQL/Criteria/NativeSQL to return the scalar values and use a
ResultTransformer(Transformers.aliasToBean(..))
to return the entity object (or DTO) with fields populated from scalar values.In my scenario, I have a Forest entity that has a collection of Tree entities with oneToMany mapping of
FetchType.EAGER
andFetchMode.JOIN
. To load only the Forest entity without loading any trees, I have used the following HQL query with scalar values and Transformers.aliasToBean(...). This works with Criteria and Native SQL as well as long as scalars and aliasToBean Transformer is used.Forest forest = (Forest) session.createQuery("select f.id as id, f.name as name, f.version as version from Forest as f where f.id=:forest").setParameter("forest", i).setResultTransformer(Transformers.aliasToBean(Forest.class)).uniqueResult();
I have tested for above simple query and it might be working checking if this works for complex cases as well and fits all your use cases.
Would be keen to know if there is a better or simpler way of doing it especially without scalars and Transformers.
If you are using JPA 2.1 (Hibernate 4.3+) you can achieve what you want with @NamedEntityGraph.
Basically, you would annotate your entity like this:
And then use the hints to fetch Person without address, like this:
More on the subject can be found here.
When you use the fetch graph only fields that you have put inside @NamedEntityGraph will be fetched eagerly.
All your existing queries that are executed without the hint will remain the same.
You didn't say why you couldn't change from eager to lazy. I, however, got the impression that it was for performance reasons and so I want to question this assumption. If that is the case, please consider the following. I realize this answer does not strictly answer your question and it violate the condition of no lazy loads, but here is an alternative that reflects my dev team's approach to what I think is the same underlying issue.
Set the fetch type to lazy, but then set a @BatchSize annotation. Since hibernate generally uses a separate DB query to load a collection, this maintains that behavior but with a tuned BatchSize, you avoid 1 query per element (while in a loop, for example) - provided your session is still open.
The behavior for the backref of a OneToOne relationship gets a little funny (the referenced side of the relationship - the side without the foreign key). But for the other side of the OneToOne, for OneToMany and ManyToOne, this gives the end result that I think you probably want: you only query the tables if you actually need them but you avoid a lazy load per record, and you don't have to explicitly configure each use case. This means that your performance will remain comparable in the event that you execute the lazy load, but this load does not happen if you do not actually need it.
After all this years, override the EAGER mapping is not yet possible on Hibernate. From the latest Hibernate documentation (5.3.7.Final):