magoffin.matt.dao.hbm.GenericHibernateDao.java Source code

Java tutorial

Introduction

Here is the source code for magoffin.matt.dao.hbm.GenericHibernateDao.java

Source

/* ===================================================================
 * GenericHibernateDao.java
 * 
 * Created Jul 2, 2006 9:36:26 AM
 * 
 * Copyright (c) 2006 Matt Magoffin.
 * 
 * This program is free software; you can redistribute it and/or 
 * modify it under the terms of the GNU General Public License as 
 * published by the Free Software Foundation; either version 2 of 
 * the License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful, 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 
 * General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License 
 * along with this program; if not, write to the Free Software 
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 
 * 02111-1307 USA
 * ===================================================================
 * $Id: GenericHibernateDao.java,v 1.9 2007/01/28 00:09:36 matt Exp $
 * ===================================================================
 */

package magoffin.matt.dao.hbm;

import java.io.Serializable;
import java.sql.SQLException;
import java.util.List;
import java.util.Map;

import magoffin.matt.dao.BatchableDao.BatchCallback;
import magoffin.matt.dao.BatchableDao.BatchCallbackResult;
import magoffin.matt.dao.BatchableDao.BatchOptions;
import magoffin.matt.dao.GenericDao;

import org.apache.log4j.Logger;
import org.hibernate.CacheMode;
import org.hibernate.Criteria;
import org.hibernate.HibernateException;
import org.hibernate.Query;
import org.hibernate.ScrollMode;
import org.hibernate.ScrollableResults;
import org.hibernate.Session;
import org.hibernate.StatelessSession;
import org.hibernate.Transaction;
import org.springframework.orm.ObjectRetrievalFailureException;
import org.springframework.orm.hibernate3.HibernateCallback;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;

/**
 * GenericDao base implementation for Hibernate 3.
 * 
 * <p>The configurable properties of this class are:</p>
 * 
 * <dl>
 *   <dt>updateMode</dt>
 *   <dd>The type of Hibernate update to perform. Defaults 
 *   to <code>UPDATE</code>.</dd>
 * </dl>
 * 
 * @param <T> the domain objec type
 * @param <PK> the primary key type
 * @author matt.magoffin
 * @version $Revision: 1.9 $ $Date: 2007/01/28 00:09:36 $
 */
public abstract class GenericHibernateDao<T, PK extends Serializable> extends HibernateDaoSupport
        implements GenericDao<T, PK> {

    /**
     * Constants for the style of Hibernate update to perform .
     */
    public enum UpdateMode {

        /** Always perform an update. */
        UPDATE,

        /** Perform merge if domain object is not a persistent object. */
        CONDITIONALLY_MERGE,

        /** Always perform merge. */
        MERGE;
    }

    /** The default batch flush count. */
    public static final int DEFAULT_BATCH_FLUSH_COUNT = 25;

    private final Class<? extends T> type;
    private UpdateMode updateMode = UpdateMode.UPDATE;
    private int batchFlushCount = DEFAULT_BATCH_FLUSH_COUNT;

    /** A class logger. */
    protected final Logger log = Logger.getLogger(getClass());

    /**
     * Constructor.
     * @param type
     */
    public GenericHibernateDao(Class<? extends T> type) {
        this.type = type;
    }

    /**
     * Get the domain object class type.
     * @return the type
     */
    protected Class<? extends T> getType() {
        return this.type;
    }

    @Override
    public void delete(T domainObject) {
        getHibernateTemplate().delete(domainObject);
    }

    @Override
    public T get(PK id) {
        try {
            return getHibernateTemplate().load(type, id);
        } catch (ObjectRetrievalFailureException e) {
            log.warn("Object not found by primary key [" + id + "]: " + e.getMessage());
        }
        return null;
    }

    @Override
    public final PK store(T domainObject) {
        PK primaryKey = getPrimaryKey(domainObject);
        if (primaryKey == null) {
            return save(domainObject);
        }
        update(domainObject);
        return primaryKey;
    }

    /**
     * Get the primary  key for a domain object.
     * @param domainObject the domain object
     * @return the primary key, or <em>null</em> if not persistant
     */
    protected abstract PK getPrimaryKey(T domainObject);

    /**
     * Persist a new domain object.
     * @param domainObject the domain object to persist
     * @return the domain object's primary key
     */
    @SuppressWarnings("unchecked")
    protected PK save(T domainObject) {
        return (PK) getHibernateTemplate().save(domainObject);
    }

    /**
     * Update a persisted domain object.
     * @param domainObject the domain object to update
     */
    protected void update(T domainObject) {
        boolean merge = false;
        if (updateMode == UpdateMode.MERGE) {
            merge = true;
        } else if (updateMode == UpdateMode.CONDITIONALLY_MERGE) {
            T persistenObject = getHibernateTemplate().get(type, getPrimaryKey(domainObject));
            if (persistenObject != domainObject) {
                merge = true;
            }
        }
        if (merge) {
            getHibernateTemplate().merge(domainObject);
        } else {
            getHibernateTemplate().update(domainObject);
        }
    }

    /**
     * Find a list of persistant objects by a named query.
     * 
     * @param queryName the name of the Hibernate query to execute
     * @return the list of results, or an empty list if none found
     */
    @SuppressWarnings("unchecked")
    protected List<T> findByNamedQuery(String queryName) {
        return getHibernateTemplate().findByNamedQuery(queryName);
    }

    /**
     * Find a list of persistant objects by a named query.
     * 
     * @param queryName the name of the Hibernate query to execute
     * @param parameters the parameters to pass to the query
     * @return the list of results, or an empty list if none found
     */
    @SuppressWarnings("unchecked")
    protected List<T> findByNamedQuery(String queryName, Object[] parameters) {
        return getHibernateTemplate().findByNamedQuery(queryName, parameters);
    }

    /**
     * Find a list of persistant objects by a named query.
     * 
     * @param queryName the name of the Hibernate query to execute
     * @param parameters the parameters to pass to the query
     * @return the list of results, or an empty list if none found
     */
    protected List<T> findByNamedQuery(final String queryName, final Map<String, Object> parameters) {
        return findByNamedQuery(queryName, parameters, 0, 0);
    }

    /**
     * Find a list of persistant objects by a named query with 
     * pagination support.
     * 
     * @param queryName the name of the Hibernate query to execute
     * @param parameters the parameters to pass to the query
     * @param page the page, starting at 0 (zero), to return
     * @param pageSize the number of results per page, or 0 for no limit
     * @return the list of results, or an empty list if none found
     */
    @SuppressWarnings("unchecked")
    protected List<T> findByNamedQuery(final String queryName, final Map<String, Object> parameters, final int page,
            final int pageSize) {
        return getHibernateTemplate().executeFind(new HibernateCallback<List<T>>() {
            @Override
            public List<T> doInHibernate(Session session) throws HibernateException, SQLException {
                Query query = session.getNamedQuery(queryName);
                if (parameters != null) {
                    for (String parameterName : parameters.keySet()) {
                        query.setParameter(parameterName, parameters.get(parameterName));
                    }
                }
                if (pageSize > 0) {
                    query.setFirstResult(pageSize * page);
                    query.setMaxResults(pageSize);
                }
                return query.list();
            }
        });
    }

    /**
     * Find a list of persistant objects by a named query with 
     * pagination support.
     * 
     * @param queryName the name of the Hibernate query to execute
     * @param parameters the parameters to pass to the query
     * @param page the page, starting at 0 (zero), to return
     * @param pageSize the number of results per page
     * @return the list of results, or an empty list if none found
     */
    @SuppressWarnings("unchecked")
    protected List<T> findByNamedQuery(final String queryName, final Object[] parameters, final int page,
            final int pageSize) {
        return getHibernateTemplate().executeFind(new HibernateCallback<List<T>>() {
            @Override
            public List<T> doInHibernate(Session session) throws HibernateException, SQLException {
                Query query = session.getNamedQuery(queryName);
                if (parameters != null) {
                    for (int i = 0; i < parameters.length; i++) {
                        query.setParameter(i, parameters[i]);
                    }
                }
                query.setFirstResult(pageSize * page);
                query.setMaxResults(pageSize);
                return query.list();
            }
        });
    }

    /**
     * Execute a batch callback using a named query.
     * 
     * @param queryName the named query name
     * @param parameters the named parameters to pass to the query
     * @param callback the callback
     * @return the number of items processed
     */
    protected Integer executeNamedQueryBatchCallback(final String queryName, final Map<String, Object> parameters,
            final BatchCallback<T> callback) {
        return getHibernateTemplate().execute(new HibernateCallback<Integer>() {
            @SuppressWarnings("unchecked")
            @Override
            public Integer doInHibernate(Session session) throws HibernateException, SQLException {
                Query q = session.getNamedQuery(queryName);
                if (parameters != null) {
                    for (String paramName : parameters.keySet()) {
                        q.setParameter(paramName, parameters.get(paramName));
                    }
                }
                q.setCacheMode(CacheMode.IGNORE);
                ScrollableResults items = q.scroll(ScrollMode.FORWARD_ONLY);
                int count = 0;

                OUTER: while (items.next()) {
                    T item = (T) items.get(0);
                    BatchCallbackResult action = callback.handle(item);
                    switch (action) {
                    case DELETE:
                        session.delete(item);
                        break;

                    case UPDATE:
                    case UPDATE_STOP:
                        store(item);
                        if (action == BatchCallbackResult.UPDATE_STOP) {
                            break OUTER;
                        }
                        break;

                    case STOP:
                        break OUTER;

                    case CONTINUE:
                        // nothing to do
                        break;
                    }
                    if (++count % batchFlushCount == 0) {
                        session.flush();
                        session.clear();
                    }
                }

                return count;
            }
        });
    }

    /**
     * Execute a batch callback against a StatelessSession using a named query.
     * 
     * <p>The DELETE, UPDATE, and UPDATE_STOP {@link BatchCallbackResult}
     * values are not supported in this operation, and will throw an 
     * <code>UnsupportedOperationException</code> if returned by the 
     * {@link BatchCallback} instance passed to this method.</p>
     * 
     * @param criteriaBuilder the criteria builder
     * @param callback the callback
     * @param options the options
     * @return the number of items processed
     */
    @SuppressWarnings("unchecked")
    protected Integer executeStatelessCriteriaBatchCallback(final CriteriaBuilder criteriaBuilder,
            final BatchCallback<T> callback, final BatchOptions options) {
        StatelessSession session = getHibernateTemplate().getSessionFactory().openStatelessSession();
        Transaction tx = session.beginTransaction();
        try {
            Criteria criteria = session.createCriteria(getType());
            criteria.setFetchSize(options.getBatchSize());
            criteriaBuilder.buildCriteria(criteria);
            ScrollableResults items = criteria.scroll(ScrollMode.FORWARD_ONLY);
            int count = 0;

            OUTER: while (items.next()) {
                T item = (T) items.get(0);
                BatchCallbackResult action = callback.handle(item);
                switch (action) {
                case DELETE:
                case UPDATE:
                case UPDATE_STOP:
                    throw new UnsupportedOperationException("Action " + action + " not possible during "
                            + options.getMode() + " mode batch processing");

                case STOP:
                    break OUTER;

                case CONTINUE:
                    // nothing to do
                    break;
                }
            }
            tx.commit();
            return count;
        } catch (RuntimeException e) {
            tx.rollback();
            throw e;
        } finally {
            if (session != null) {
                session.close();
            }
        }
    }

    /**
     * Execute a batch callback against a normal Hibernate Session using a named query.
     * 
     * @param criteriaBuilder the criteria builder
     * @param callback the callback
     * @param options the options
     * @return the number of items processed
     */
    protected Integer executeLiveCriteriaBatchCallback(final CriteriaBuilder criteriaBuilder,
            final BatchCallback<T> callback, final BatchOptions options) {
        return getHibernateTemplate().execute(new HibernateCallback<Integer>() {
            @SuppressWarnings("unchecked")
            @Override
            public Integer doInHibernate(Session session) throws HibernateException, SQLException {
                Criteria criteria = session.createCriteria(type);
                criteria.setFetchSize(options.getBatchSize());
                criteriaBuilder.buildCriteria(criteria);
                ScrollableResults items = criteria.scroll(ScrollMode.FORWARD_ONLY);
                int count = 0;

                OUTER: while (items.next()) {
                    T item = (T) items.get(0);
                    BatchCallbackResult action = callback.handle(item);
                    switch (action) {
                    case DELETE:
                        session.delete(item);
                        break;

                    case UPDATE:
                    case UPDATE_STOP:
                        store(item);
                        if (action == BatchCallbackResult.UPDATE_STOP) {
                            break OUTER;
                        }
                        break;

                    case STOP:
                        break OUTER;

                    case CONTINUE:
                        // nothing to do
                        break;
                    }
                    if (++count % 20 == 0) {
                        session.flush();
                        session.clear();
                    }
                }

                return count;
            }
        });
    }

    /**
     * @return the updateMode
     */
    public UpdateMode getUpdateMode() {
        return updateMode;
    }

    /**
     * @param updateMode the updateMode to set
     */
    public void setUpdateMode(UpdateMode updateMode) {
        this.updateMode = updateMode;
    }

    /**
     * @return the batchFlushCount
     */
    protected int getBatchFlushCount() {
        return batchFlushCount;
    }

    /**
     * @param batchFlushCount the batchFlushCount to set
     */
    protected void setBatchFlushCount(int batchFlushCount) {
        this.batchFlushCount = batchFlushCount;
    }

}