I'm getting a warning in the Server log "firstResult/maxResults specified with collection fetch; applying in memory!". However everything working fine. But I don't want this warning.
My code is
public employee find(int id) {
return (employee) getEntityManager().createQuery(QUERY).setParameter("id", id).getSingleResult();
}
My query is
QUERY = "from employee as emp left join fetch emp.salary left join fetch emp.department where emp.id = :id"
Although you are getting valid results, the SQL query fetches all data and it's not as efficient as it should.
As I explained in this article, you have two options.
Fixing the issue with two SQL queries that can fetch entities in read-write mode
The easiest way to fix this issue is to execute two queries:
. The first query will fetch the root entity identifiers matching the provided filtering criteria. . The second query will use the previously extracted root entity identifiers to fetch the parent and the child entities.
This approach is very easy to implement and looks as follows:
Fixing the issue with one SQL query that can only fetch entities in read-only mode
The second approach is to use SDENSE_RANK over the result set of parent and child entities that match our filtering criteria and restrict the output for the first N post entries only.
The SQL query can look as follows:
The
@NamedNativeQuery
fetches all Post entities matching the provided title along with their associatedPostComment
child entities. TheDENSE_RANK
Window Function is used to assign the rank for each Post andPostComment
joined record so that we can later filter just the amount of Post records we are interested in fetching.The
SqlResultSetMapping
provides the mapping between the SQL-level column aliases and the JPA entity properties that need to be populated.Now, we can execute the
PostWithCommentByRank
@NamedNativeQuery
like this:Now, by default, a native SQL query like the
PostWithCommentByRank
one would fetch the Post and thePostComment
in the same JDBC row, so we will end up with an Object[] containing both entities.However, we want to transform the tabular
Object[]
array into a tree of parent-child entities, and for this reason, we need to use the HibernateResultTransformer
For more details about theResultTransformer
, check out this article.The
DistinctPostResultTransformer
looks as follows:The
DistinctPostResultTransformer
must detach the entities being fetched because we are overwriting the child collection and we don’t want that to be propagated as an entity state transition:Fore more details, check out this article.
I guess the emp has many departments which is a One to Many relationship. Hibernate will fetch many rows for this query with fetched department records. So the order of result set can not be decided until it has really fetch the results to the memory. So the pagination will be done in memory.
If you do not want to fetch the departments with emp, but still want to do some query based on the department, you can achieve the result with out warning (without doing ordering in the memory). For that simply you have to remove the "fetch" clause. So something like as follows:
QUERY = "from employee as emp left join emp.salary sal left join emp.department dep where emp.id = :id and dep.name = 'testing' and sal.salary > 5000 "
Reason for this warning is that when fetch join is used, order in result sets is defined only by ID of selected entity (and not by join fetched).
If this sorting in memory is causing problems, do not use firsResult/maxResults with JOIN FETCH.
the problem is you will get cartesian product doing JOIN. The offset will cut your recordset without looking if you are still on same root identity class
To avoid this WARNING you have to change the call
getSingleResult
togetResultList().get(0)