org.jdal.dao.hibernate.HibernateDao.java Source code

Java tutorial

Introduction

Here is the source code for org.jdal.dao.hibernate.HibernateDao.java

Source

/*
 * Copyright 2009-2015 Jose Luis Martin.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.jdal.dao.hibernate;

import java.io.Serializable;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.Criteria;
import org.hibernate.HibernateException;
import org.hibernate.LockOptions;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.Example;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Projections;
import org.hibernate.impl.CriteriaImpl;
import org.hibernate.metadata.ClassMetadata;
import org.hibernate.transform.ResultTransformer;
import org.hibernate.type.Type;
import org.jdal.beans.PropertyUtils;
import org.jdal.dao.DaoSupport;
import org.jdal.dao.Filter;
import org.jdal.dao.Page;
import org.jdal.hibernate.HibernateUtils;
import org.springframework.beans.PropertyAccessor;
import org.springframework.orm.ObjectRetrievalFailureException;
import org.springframework.orm.hibernate3.HibernateTemplate;
import org.springframework.util.ClassUtils;

/**
 * Hibernate generic DAO implementation. Support pagination of results and filters
 * using the {@link #getPage(Page)} method.
 *
 * @author Jose Luis Martin
 * @see org.jdal.dao.Dao
 * @since 1.0
 */
public class HibernateDao<T, PK extends Serializable> extends DaoSupport<T, PK> {

    private static final Log log = LogFactory.getLog(HibernateDao.class);

    private Class<T> entityClass;
    private boolean cachePageQueries = false;
    private HibernateTemplate hibernateTemplate;

    /** 
     * Synchronized map with CriteriaBuilders by name
     */
    private Map<String, CriteriaBuilder> criteriaBuilderMap = Collections
            .synchronizedMap(new HashMap<String, CriteriaBuilder>());

    public HibernateDao() {
    }

    /**
     * @param persistentClass
     */
    public HibernateDao(Class<T> persistentClass) {
        this.entityClass = persistentClass;
    }

    /**
     * Get Page, apply filter if any.
     * If Filter is a entity model, use Example to create a criteria.
     * else enable filter by name on session. 
     * @param page with page definitions
     * @return page of results
     */
    @SuppressWarnings({ "unchecked", "rawtypes" })
    public <K> Page<K> getPage(Page<K> page) {

        List data = null;
        // try named query
        Query query = getQuery(page);
        if (query != null) {
            data = query.list();
        } else {
            // try filter, example and criteria builders
            Criteria criteria = getCriteria(page);
            ResultTransformer rt = ((CriteriaImpl) criteria).getResultTransformer();
            criteria.setProjection(Projections.rowCount());
            page.setCount(((Long) criteria.uniqueResult()).intValue());
            // reset criteria
            criteria.setProjection(null);
            criteria.setResultTransformer(rt);
            // set start index and page size
            criteria.setFirstResult(page.getStartIndex()).setMaxResults(page.getPageSize());
            applyOrder(page, criteria);
            // run it
            criteria.setCacheable(cachePageQueries);
            data = criteria.list();
        }

        page.setData(data);

        return page;
    }

    /**
     * Get Hibernate named Query and configure with filter from page. 
     * Set the result count on page also. 
     * @param page page 
     * @return Hibernate named Query.
     */
    private Query getQuery(Page<?> page) {
        Object filter = page.getFilter();

        try {
            if (filter instanceof Filter) {
                Filter f = (Filter) filter;
                Query query = getSession().getNamedQuery(f.getFilterName());
                Query countQuery = getSession().createQuery(query.getQueryString().replaceFirst("select", "count"));
                query.setProperties(f.getParameterMap());
                query.setMaxResults(page.getPageSize());
                query.setFirstResult(page.getStartIndex());
                page.setCount((Integer) countQuery.uniqueResult());

                return query;
            }
        } catch (HibernateException e) {
        }
        return null;
    }

    /**
     * Create a Criteria from filter. If filter is a entity class instance, 
     * return a Criteria with a Example Criterion applied.
     * If not try four options in order:
     * 
     *    1. if there are a filter with name filter.getName() enable it and return criteria
     *  2. if there are a criteria builder with name filter.getName() use it to create Critera.
     *  4. if there are a method named createCritera + filter.getName() invoke it to create Criteria
     *  5. Return a Criteria for this entity class without Criterion applied.
     * 
     * @param filter the filter
     * @return a new Criteria 
     */
    protected Criteria getCriteria(Page<?> page) {
        Criteria executableCriteria = getSession().createCriteria(getEntityClass());
        Object filter = page.getFilter();
        // apply filter, if any
        if (filter != null) {
            if (ClassUtils.isAssignable(getEntityClass(), filter.getClass())) {
                // try a findByExample...
                executableCriteria.add(Example.create(getEntityClass()));
            } else if (filter instanceof Filter) {
                Filter f = (Filter) filter;

                if (!enableFilter(f)) {
                    if (log.isDebugEnabled())
                        log.debug("No hibernate filter found with name: " + f.getFilterName()
                                + ", try criteria builder.");
                    // if no filter, try criteria builder
                    if (criteriaBuilderMap.containsKey(f.getFilterName())) {
                        CriteriaBuilder cb = criteriaBuilderMap.get(f.getFilterName());
                        if (log.isDebugEnabled())
                            log.debug("Found criteria builder with name: " + f.getFilterName()
                                    + " - criteria builder class: " + cb.getClass().getSimpleName());
                        executableCriteria = cb.build(executableCriteria, f);
                    }
                    // if no criteria builder try subclass method
                    else if (ClassUtils.hasMethod(getClass(), "createCriteria" + f.getFilterName(),
                            new Class[] { Criteria.class })) {
                        Method method = ClassUtils.getMethodIfAvailable(getClass(),
                                "createCriteria" + f.getFilterName(), new Class[] { Criteria.class });
                        if (method != null) {
                            try {
                                executableCriteria = (Criteria) method.invoke(this, executableCriteria);
                            } catch (Exception e) {
                                log.error(e);
                            }
                        }
                    }
                }
            } else {
                log.warn("Cannot manage filter of type: " + filter.getClass());
            }
        }

        return executableCriteria;
    }

    /**
     * Enable predefined filter in current session
     * @param f Filter with filter name and parameters
     * @return true if hibernate filter exists
     */
    public boolean enableFilter(Filter f) {
        if (getSessionFactory().getDefinedFilterNames().contains(f.getFilterName())) {
            org.hibernate.Filter hf = getSession().enableFilter(f.getFilterName());
            Map<String, Object> parameterMap = f.getParameterMap();
            for (String key : parameterMap.keySet()) {
                hf.setParameter(key, parameterMap.get(key));
            }
            return true;
        }

        return false;
    }

    /**
     * @return ClassMetadata from entityClass
     */
    private ClassMetadata getClassMetadata() {
        return getClassMetadata(getEntityClass());
    }

    /**
     * return ClassMetadata from Class
     * @param clazz the class
     * @return the ClassMetadata
     */
    private ClassMetadata getClassMetadata(Class<?> clazz) {
        return getHibernateTemplate().getSessionFactory().getClassMetadata(clazz);
    }

    /**
     * Apply Order to Criteria
     * @param page the page
     * @param criteria the criteria
     */
    protected void applyOrder(Page<?> page, Criteria criteria) {
        Order order = createOrder(criteria, page.getSortName(), Page.Order.ASC.equals(page.getOrder()));
        if (order != null)
            criteria.addOrder(order);
    }

    /** 
     * Create Order from criteria and property path
     * @param criteria the hibernate criteria to apply order on
     * @param propertyPath the property path
     * @return Order 
     */
    protected Order createOrder(Criteria criteria, String propertyPath, boolean ascending) {
        Order order = null;

        if (propertyPath != null) {
            String sortProperty = PropertyUtils.getPropertyName(propertyPath);
            try {
                if (PropertyUtils.isNested(propertyPath)) {
                    String aliasPropertyPath = PropertyUtils.getPath(propertyPath);

                    // Need to create alias?
                    String alias = HibernateUtils.findAliasForPropertyPath(criteria, propertyPath);
                    if (alias == null) {
                        alias = PropertyUtils.getPropertyName(aliasPropertyPath);
                        criteria.createAlias(aliasPropertyPath, alias);
                    }
                    sortProperty = alias + PropertyUtils.PROPERTY_SEPARATOR + sortProperty;
                } else { // test if property is an entity class
                    Type sortType = getClassMetadata().getPropertyType(propertyPath);
                    if (sortType.isEntityType()) { // is entity, look for 'name' property
                        String[] propertyNames = getClassMetadata(sortType.getReturnedClass()).getPropertyNames();
                        for (String name : propertyNames) {
                            if ("name".equals(name)) {
                                log.info("Found property name on persistent class: " + sortType.getName());
                                String newPath = propertyPath + PropertyAccessor.NESTED_PROPERTY_SEPARATOR + "name";
                                return createOrder(criteria, newPath, ascending);
                            }
                        }
                    }
                }

                if (log.isDebugEnabled())
                    log.debug("Setting order as: " + sortProperty);

                order = ascending ? Order.asc(sortProperty) : Order.desc(sortProperty);
            } catch (HibernateException he) {
                log.error("Cannot to create Order for property: " + sortProperty + " for "
                        + getEntityClass().getSimpleName(), he);
            }
        } else {
            // add default order by id
            ClassMetadata metadata = getClassMetadata();
            if (metadata != null)
                order = Order.asc(metadata.getIdentifierPropertyName());
        }

        return order;

    }

    /**
     * @return the entityClass
     */
    public Class<T> getEntityClass() {
        return entityClass;
    }

    /**
     * @param entityClass the entityClass to set
     */
    public void setEntityClass(Class<T> entityClass) {
        this.entityClass = entityClass;
    }

    /** 
     * Delete a entity from db
     * @param entity
     */
    public void delete(T entity) {
        getHibernateTemplate().delete(entity);
    }

    public T save(T entity) {
        getHibernateTemplate().saveOrUpdate(entity);
        return entity;
    }

    @SuppressWarnings("unchecked")
    public List<Serializable> getKeys(Page<T> page) {
        Criteria criteria = getCriteria(page);
        return criteria.setProjection(Projections.id()).list();

    }

    public void deleteById(PK id) {
        getSession().delete(get(id));
    }

    public T initialize(T entity) {
        getSession().buildLockRequest(LockOptions.NONE).lock(entity);
        HibernateUtils.initialize(getSessionFactory(), entity);
        return entity;
    }

    public T initialize(T entity, int depth) {
        getSession().buildLockRequest(LockOptions.NONE).lock(entity);
        HibernateUtils.initialize(getSessionFactory(), entity, depth);

        return entity;
    }

    /**
     * @return the criteriaBuilderMap
     */
    public Map<String, CriteriaBuilder> getCriteriaBuilderMap() {
        return criteriaBuilderMap;
    }

    /**
     * @param criteriaBuilderMap the criteriaBuilderMap to set
     */
    public void setCriteriaBuilderMap(Map<String, CriteriaBuilder> criteriaBuilderMap) {
        this.criteriaBuilderMap.clear();
        this.criteriaBuilderMap.putAll(criteriaBuilderMap);
    }

    /**
      * {@inheritDoc}
      */
    public List<T> getAll() {
        return new ArrayList<T>(getHibernateTemplate().loadAll(this.entityClass));
    }

    public List<T> getAllDistinct() {
        Collection<T> result = new LinkedHashSet<T>(getAll());
        return new ArrayList<T>(result);
    }

    /** 
     * {@inheritDoc}
     */
    @SuppressWarnings("unchecked")
    public List<T> findByNamedQuery(String queryName, Map<String, Object> queryParams) {
        String[] params = new String[queryParams.size()];
        Object[] values = new Object[queryParams.size()];
        int index = 0;
        Iterator<String> i = queryParams.keySet().iterator();
        while (i.hasNext()) {
            String key = i.next();
            params[index] = key;
            values[index++] = queryParams.get(key);
        }
        return (List<T>) getHibernateTemplate().findByNamedQueryAndNamedParam(queryName, params, values);
    }

    /**
     * {@inheritDoc}
     */
    public T get(PK id) {
        T entity = (T) getHibernateTemplate().get(this.entityClass, id);

        if (entity == null) {
            log.warn("'" + this.entityClass.getSimpleName() + "' object with id '" + id + "' not found...");
            throw new ObjectRetrievalFailureException(this.entityClass, id);
        }

        return entity;
    }

    /**
     * {@inheritDoc}
     */
    public boolean exists(PK id) {
        return getHibernateTemplate().get(this.entityClass, id) != null;
    }

    /**
     * Count rows
     * @return number of rows
     */
    public int count() {
        return ((Long) getSession().createCriteria(entityClass).setProjection(Projections.rowCount())
                .uniqueResult()).intValue();
    }

    /**
     * @return the cachePageQueries
     */
    public boolean isCachePageQueries() {
        return cachePageQueries;
    }

    /**
     * @param cachePageQueries the cachePageQueries to set
     */
    public void setCachePageQueries(boolean cachePageQueries) {
        this.cachePageQueries = cachePageQueries;
    }

    /**
     * {@inheritDoc}
     */
    @SuppressWarnings("unchecked")
    public <E> E get(PK id, Class<E> clazz) {
        return (E) getSession().get(clazz, id);
    }

    /**
     * {@inheritDoc}
     */
    @SuppressWarnings("unchecked")
    public <E> List<E> getAll(Class<E> clazz) {
        return getSession().createCriteria(clazz).list();
    }

    public HibernateTemplate getHibernateTemplate() {
        return hibernateTemplate;
    }

    public void setHibernateTemplate(HibernateTemplate hibernateTemplate) {
        this.hibernateTemplate = hibernateTemplate;
    }

    public final void setSessionFactory(SessionFactory sessionFactory) {
        if (this.hibernateTemplate == null || sessionFactory != this.hibernateTemplate.getSessionFactory()) {
            this.hibernateTemplate = new HibernateTemplate(sessionFactory);
        }
    }

    /**
     * Return the Hibernate SessionFactory
     */
    public final SessionFactory getSessionFactory() {
        return (this.hibernateTemplate != null ? this.hibernateTemplate.getSessionFactory() : null);
    }

    /**
     * Return current hibernate Session from Hibernate template
     * @return curren hibernate Session
     */
    public Session getSession() {
        return hibernateTemplate.getSessionFactory().getCurrentSession();
    }
}