Dear all,
I have a PHP application with lots of MySQL statements for which the number of rows retrieved by the statements (ignoring the limit keyword) is needed.
These MySQL statements make use of the SQL_CALC_FOUND_ROWS keyword.
Here is one such statement:
Code:
function selectAll($start = false, $rows = false) {
$sqlStatement = "SELECT SQL_CALC_FOUND_ROWS * FROM $this->tableName ORDER BY name";
if ($rows) {
if (!$start) {
$start = 0;
}
$sqlStatement .= " LIMIT " . $start . ", " . $rows;
} else if ($start) {
$sqlStatement .= " LIMIT " . $start;
}
return($this->querySelect($sqlStatement));
}
When this statement is performed, it returns a limited number of rows.
And right after that statement, this other statement is performed:
Code:
// Count the number of rows of the last select statement
// ignoring the LIMIT keyword if any
// The SQL_CALC_FOUND_ROWS clause tells MySQL to calculate how many rows there would be
// in the result set, disregarding any LIMIT clause with the number of rows later
// retrieved using the SELECT FOUND_ROWS() statement
function countFoundRows() {
$sqlStatement = "SELECT FOUND_ROWS() as count";
return($this->querySelect($sqlStatement));
}
There are lots of such statements in the application so as to offer pagination support.
Now, I wonder how to do this in Hibernate.
I have the following dao classes:
Code:
@Transactional
public class AdminHibernateDao extends GenericHibernateDao<Admin, Serializable> implements AdminDao {
@SuppressWarnings("unchecked")
@Override
public List<Admin> findLikePattern(String pattern) {
pattern = "%" + pattern + "%";
Criteria criteria = getSession().createCriteria(Admin.class);
criteria.add(Restrictions.ilike("firstname", pattern))
.add(Restrictions.ilike("lastname", pattern))
.add(Restrictions.ilike("login", pattern))
.add(Restrictions.ilike("email", pattern))
.addOrder(Order.asc("firstname")).addOrder(Order.asc("lastname"));
return criteria.list();
}
}
Code:
@Transactional
public abstract class GenericHibernateDao<T, ID extends Serializable> implements GenericDao<T, ID> {
private static Logger log = Logger.getLogger(GenericHibernateDao.class);
private Class<T> persistentClass;
private SessionFactory sessionFactory;
@SuppressWarnings("unchecked")
public GenericHibernateDao() {
this.persistentClass = (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
}
public void setSessionFactory(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
}
public SessionFactory getSessionFactory() {
if (sessionFactory == null) {
throw new IllegalStateException("sessionFactory has not been set on DAO before usage");
}
return sessionFactory;
}
public Class<T> getPersistentClass() {
return persistentClass;
}
@Override
public Session getSession() {
return getSessionFactory().getCurrentSession();
}
@SuppressWarnings("unchecked")
@Override
public T findById(ID id, boolean lock) {
T entity;
if (lock)
entity = (T) getSession().load(getPersistentClass(), id, LockMode.UPGRADE);
else
entity = (T) getSession().load(getPersistentClass(), id);
return entity;
}
@SuppressWarnings("unchecked")
@Override
public boolean isFoundById(ID id) {
try {
@SuppressWarnings("unused")
T entity = (T) getSession().load(getPersistentClass(), id);
} catch (ObjectNotFoundException e) {
return false;
}
return true;
}
@Override
public List<T> findAll() {
return findObjectsByCriteria();
}
@Override
public long countAllRows() {
Criteria criteria = getSession().createCriteria(getPersistentClass());
criteria.setProjection(Projections.rowCount());
Integer count = (Integer) criteria.list().get(0);
long itemCount = count.intValue();
return itemCount;
}
@Override
public long countFoundRows() {
Criteria criteria = getSession().createCriteria(getPersistentClass());
criteria.setProjection(Projections.rowCount());
Integer count = (Integer) criteria.uniqueResult();
long itemCount = count.intValue();
return itemCount;
}
@SuppressWarnings("unchecked")
@Override
public List<T> findByExample(T exampleInstance, String... excludeProperty) {
Criteria criteria = getSession().createCriteria(getPersistentClass());
Example example = Example.create(exampleInstance);
for (String exclude : excludeProperty) {
example.excludeProperty(exclude);
}
criteria.add(example);
return criteria.list();
}
@Override
public T makePersistent(T entity) {
getSession().saveOrUpdate(entity);
return entity;
}
@Override
public void makeTransient(T entity) {
getSession().delete(entity);
}
public void flush() {
getSession().flush();
}
public void clear() {
getSession().clear();
}
@SuppressWarnings("unchecked")
@Override
public List<T> findObjectsByCriteria(Criterion... criterion) {
Criteria criteria = getSession().createCriteria(getPersistentClass());
for (Criterion c : criterion) {
criteria.add(c);
}
return criteria.list();
}
@Override
public T findObjectByCriteria(Criterion... criterion) {
T object = null;
List<T> results = findObjectsByCriteria(criterion);
if (results.isEmpty()) {
if (log.isDebugEnabled()) {
log.debug("No search results found for: " + criterion);
}
} else {
if (results.size() > 1) {
if (log.isDebugEnabled()) {
log.debug("The criterion : " + criterion + " should return only one result");
}
}
object = results.get(0);
}
return object;
}
@SuppressWarnings("unchecked")
@Override
public T findObjectByCriteria(Criteria criteria) {
T object = null;
List<T> results = criteria.list();
if (results.isEmpty()) {
if (log.isDebugEnabled()) {
log.debug("No search results found for the criteria : " + criteria);
}
} else {
if (results.size() > 1) {
if (log.isDebugEnabled()) {
log.debug("The criteria : " + criteria + " should return only one result");
}
}
object = results.get(0);
}
return object;
}
}
I would like to offer pagination on the findLikePattern statement.
How to go to have all my findBy.. statements offering the number of found rows ?
Do you know of any strategy to have this feature on hundreds of different findBy... statements ?