Usually pagination queries look like this. Is there a better way instead of making two almost equal methods, one of which executing \"select *...\" and the other one \"count
I know this problem and have faced it before. For starters, the double query mechanism where it does the same SELECT conditions is indeed not optimal. But, it works, and before you go off and do some giant change, just realize it might not be worth it.
But, anyways:
1) If you are dealing with small data on the client side, use a result set implementation that lets you set the cursor to the end of the set, get its row offset, then reset the cursor to before first.
2) Redesign the query so that you get COUNT(*) as an extra column in the normal rows. Yes, it contains the same value for every row, but it only involves 1 extra column that is an integer. This is improper SQL to represent an aggregated value with non aggregated values, but it may work.
3) Redesign the query to use an estimated limit, similar to what was being mentioned. Use rows per page and some upper limit. E.g. just say something like "Showing 1 to 10 of 500 or more". When they browse to "Showing 25o to 260 of X", its a later query so you can just update the X estimate by making the upper bound relative to page * rows/page.
I found a way to do paging in hibernate without doing a select count (*) over a large dataset size. Look at the solution that I posted for my answer here.
processing a large number of database entries with paging slows down with time
you can perform paging one at a time without knowing how many pages you will need originally
I think the solution depends on database you are using. For example, we are using MS SQL and using next query
select
COUNT(Table.Column) OVER() as TotalRowsCount,
Table.Column,
Table.Column2
from Table ...
That part of query can be changed with database specified SQL.
Also we set the query max result we are expecting to see, e.g.
query.setMaxResults(pageNumber * itemsPerPage)
And gets the ScrollableResults instance as result of query execution:
ScrollableResults result = null;
try {
result = query.scroll();
int totalRowsNumber = result.getInteger(0);
int from = // calculate the index of row to get for the expected page if any
/*
* Reading data form page and using Transformers.ALIAS_TO_ENTITY_MAP
* to make life easier.
*/
}
finally {
if (result != null)
result.close()
}
Here is a solution by Dr Richard Kennard (mind the bug fix in the blog comment!), using Hibernate Interceptors
For summary, you bind your sessionFactory to your interceptor class, so that your interceptor can give you the number of found rows later.
You can find the code on the solution link. And below is an example usage.
SessionFactory sessionFactory = ((org.hibernate.Session) mEntityManager.getDelegate()).getSessionFactory();
MySQLCalcFoundRowsInterceptor foundRowsInterceptor = new MySQLCalcFoundRowsInterceptor( sessionFactory );
Session session = sessionFactory.openSession( foundRowsInterceptor );
try {
org.hibernate.Query query = session.createQuery( ... ) // Note: JPA-QL, not createNativeQuery!
query.setFirstResult( ... );
query.setMaxResults( ... );
List entities = query.list();
long foundRows = foundRowsInterceptor.getFoundRows();
...
} finally {
// Disconnect() is good practice, but close() causes problems. Note, however, that
// disconnect could lead to lazy-loading problems if the returned list of entities has
// lazy relations
session.disconnect();
}
At this Hibernate wiki page:
https://www.hibernate.org/314.html
I present a complete pagination solution; in particular, the total number of elements is computed by scrolling to the end of the resultset, which is supported by now by several JDBC drivers. This avoids the second "count" query.