/********************************************************************
 * Copyright © 2004 VHA. All rights reserved
 ********************************************************************/
package gov.va.med.fw.persistent.hibernate;

// Java classes
import java.io.Serializable;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang.Validate;
import org.apache.commons.lang.builder.ToStringBuilder;
import org.apache.commons.lang.exception.ExceptionUtils;
import org.hibernate.Query;

import org.hibernate.Session;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.support.SQLExceptionTranslator;
import org.springframework.orm.hibernate3.HibernateCallback;

import gov.va.med.fw.persistent.DAOException;
import gov.va.med.fw.persistent.DAOOperations;
import gov.va.med.fw.persistent.QueryIncrementTracker;
import gov.va.med.fw.persistent.QueryInfo;
import gov.va.med.fw.persistent.ScrollableCallback;

/**
 * Extends from an abstract dao class to provide additional convenient methods
 * to execute hql statements. An abtract dao class is aimed to serve as a base
 * class for specific DAO that has a one-to-one relationship to a persistent
 * entity. For instance, a person entity can be persisted, queried, or updated
 * through its PersonDAO class and named queries are automatically looked up in
 * the person's hibernate mapping file.</br></br> This generic DAO provides
 * generic usages for persisting any entity. Even though this implementation
 * uses Hibernate technology, specific Hibernate classes are encapsulated from
 * a caller to promote loose coupling. hence, to improve flexibility </b>
 *
 * Project: Framework
 *
 * @author DNS   LEV
 * @version 1.0
 */
public class GenericDAOImpl extends AbstractDAOImpl implements DAOOperations {

   /**
	 * An instance of serialVersionUID
	 */
	private static final long serialVersionUID = 2927972729631888104L;

	/**
    * A default constructor
    */
   public GenericDAOImpl() {
      super();
   }

   /** Executes a generic data base operation
    *
    * @param action An action to be executed in a session
    * @return A return value from an action
    * @throws DAOException thrown if failed to execute an action
    */
   public Object execute( AbstractDAOAction action) throws DAOException {
      try {
         return this.getHibernateTemplate().execute( action );
      }
      catch( RuntimeException e ) {
         throw new DAOException( "Failed to execute an action", e );
      }
   }

   /**
    * Clear all data in the current session
    * @throws DAOException Thrown if failed to clear a sesion
    */
   public void clear() throws DAOException {
      try {
         this.getHibernateTemplate().clear();
      }
      catch( RuntimeException e ) {
         throw new DAOException( "Failed to clear a session", e );
      }
   }

   /** Closes an iterator of result set generated from a database operation
    *
    * @param it An iterator to be closed
    * @throws DAOException Thrown if failed to close an iterator
    */
   public void closeIterator(Iterator it) throws DAOException {
      try {
         Validate.notNull( it, "An iterator must not be null" );
      	this.getHibernateTemplate().closeIterator( it );
	   }
	   catch( RuntimeException e ) {
	      throw new DAOException( "Failed to close an iterator", e );
	   }
   }

   /** Check if an entity exist in the Hibernate session
    *
    * @param entity An entity to check for existence in a session
    * @return true if an entity is in the session. false otherwise
    * @throws DAOException thrown if failed to check for an entity
    */
   public boolean contains(Object entity) throws DAOException {
      try {
         return this.getHibernateTemplate().contains( entity );
	   }
	   catch( RuntimeException e ) {
	      throw new DAOException( "Failed to check if an entity is in a sesion", e );
	   }
   }

   /** Deletes a collection of entities from a database
    *
    * @param entities A collection to be deleted
    * @throws DAOException thrown if failed to delete from a database
    */
   public void deleteAll(Collection entities) throws DAOException {
      try {
         Validate.notEmpty( entities );
         this.getHibernateTemplate().deleteAll( entities );
	   }
	   catch( RuntimeException e ) {
	      throw new DAOException( "Failed to delete all entities", e );
	   }
   }

   /** Evicts an entity from a session. This method detaches a persistent
    * entity from a session.
    *
    * @param entity An entity to be detach from a session
    * @throws DAOException Thrown if failed to detach an entity from a session
    */
   public void evict(Object entity) throws DAOException {
      try {
         if( this.contains( entity ) ) {
            this.getHibernateTemplate().evict( entity );
         }
	   }
	   catch( RuntimeException e ) {
	      throw new DAOException( "Failed to detach an entity from a session", e );
	   }
   }

	/**
	 * Execute the action specified by the given action object within a Session.
	 * @param action callback object that specifies the Hibernate action
	 * @param exposeNativeSession whether to expose the native Hibernate Session
	 * to callback code
	 * @return a result object returned by the action, or null
	 * @throws DAOException in case of Hibernate errors
	 */
   public Object execute(AbstractDAOAction action, boolean exposeNativeSession) throws DAOException {
      try {
         Validate.notNull( action );
         return this.getHibernateTemplate().execute( action, exposeNativeSession );
	   }
	   catch( RuntimeException e ) {
	      throw new DAOException( "Failed to execute an action", e );
	   }
   }

	/**
	 * Execute the action specified by the given action object within a Session.
	 * @param action callback object that specifies the Hibernate action
	 * @param exposeNativeSession whether to expose the native Hibernate Session
	 * to callback code
	 * @return a result object returned by the action, or null
	 * @throws DAOException in case of Hibernate errors
	 */
   public List executeFind(AbstractDAOAction action) throws DAOException {
      return (List)this.execute( action, this.isExposeNativeSession() );
   }

   /** Execute a query for persistent instances, binding one value to a "?"
    * parameter in the query string
    *
    * @param queryString A query expression
    * @param value A value of a parameter
    * @return A result entity
    * @throws DAOException in case of Hibernate errors
    */
   public List find(String queryString, Object value) throws DAOException {
      try {
         Validate.notNull( queryString );
         return this.getHibernateTemplate().find( queryString, value );
	   }
	   catch( RuntimeException e ) {
	      StringBuffer info = new StringBuffer();
	      info.append( "Failed to execute a query: " )
	      	 .append( queryString )
	      	 .append( " value: " )
	      	 .append( value );

	      throw new DAOException( info.toString(), e );
	   }
   }

   /** Execute a query for persistent instances, binding an array of values to "?"
    * parameters in the query string
    *
    * @param queryString A query expression
    * @param values An array of parameter values
    * @return A result entity
    * @throws DAOException in case of Hibernate errors
    */
   public List find(String queryString, Object[] values) throws DAOException {
      try {
         Validate.notNull( queryString );
         return this.getHibernateTemplate().find( queryString, values );
	   }
	   catch( RuntimeException e ) {
	      StringBuffer info = new StringBuffer();
	      info.append( "Failed to execute a query: " )
	      	 .append( queryString )
	      	 .append( " values: " )
	      	 .append( ToStringBuilder.reflectionToString( values ) );

	      throw new DAOException( info.toString(), e );
	   }
   }

   /**
    * @param queryName
    * @param paramName
    * @param value
    * @return
    * @throws org.springframework.dao.DAOException
    */
   public List findByNamedParam(String queryName, String paramName, Object value)
            throws DAOException {
      try {
         Validate.notNull( queryName );
         Validate.notNull( paramName );

         //CCR12710
 		if( logger.isDebugEnabled() ) {
             logger.debug( "GenericDAOImpl findByNamedParam:" + queryName + " param: " + paramName + " " + value );
          }

         return this.getHibernateTemplate().findByNamedParam( queryName, paramName, value );
	   }
	   catch( RuntimeException e ) {
	      StringBuffer info = new StringBuffer();
	      info.append( "Failed to execute a query by named query: " )
	      	 .append( queryName )
	      	 .append( " param Name: " )
	      	 .append( paramName )
	      	 .append( " value: " )
	      	 .append( ToStringBuilder.reflectionToString( value ) );

	      throw new DAOException( info.toString(), e );
	   }
   }

   /**
    * @param queryString
    * @param paramNames
    * @param values
    * @return
    * @throws org.springframework.dao.DAOException
    */
   public List findByNamedParam(String queryString, String[] paramNames,
            Object[] values) throws DAOException {
      try {
         Validate.notNull( queryString, "A qyery string must not be null" );
         return this.getHibernateTemplate().findByNamedParam( queryString, paramNames, values );
	   }
	   catch( RuntimeException e ) {
	      StringBuffer info = new StringBuffer();
	      info.append( "Failed to execute a query by named param: " )
	      	 .append( queryString )
	      	 .append( " param Names: " )
	      	 .append( ToStringBuilder.reflectionToString( paramNames ) )
	      	 .append( " values: " )
	      	 .append( ToStringBuilder.reflectionToString( values ) );

	      throw new DAOException( info.toString(), e );
	   }
   }

   /**
    * @param queryName
    * @param value
    * @return
    * @throws org.springframework.dao.DAOException
    */
   public List findByNamedQuery(String queryName, Object value) throws DAOException {

      try {
         Validate.notNull( queryName, "A qyery string's name must not be null" );
         //CCR12710
  		if( logger.isDebugEnabled() ) {
              logger.debug( "GenericDAOImpl findByNamedQuery:" + queryName);
           }
         return this.getHibernateTemplate().findByNamedQuery( queryName, value );
	   }
	   catch( RuntimeException e ) {
	      StringBuffer info = new StringBuffer();
	      info.append( "Failed to execute a query by named param: " )
	      	 .append( queryName )
	      	 .append( " values: " )
	      	 .append( ToStringBuilder.reflectionToString( value ) );

	      throw new DAOException( info.toString(), e );
	   }
   }

   /**
    * @param queryName
    * @param values
    * @return
    * @throws org.springframework.dao.DAOException
    */
   public List findByNamedQuery(String queryName, Object[] values) throws DAOException {
      try {
         Validate.notNull( queryName, "A qyery string's name must not be null" );
         return this.getHibernateTemplate().findByNamedQuery( queryName, values );
	   }
	   catch( RuntimeException e ) {
	      StringBuffer info = new StringBuffer();
	      info.append( "Failed to execute a query by named param: " )
	      	 .append( queryName )
	      	 .append( " valuess: " )
	      	 .append( ToStringBuilder.reflectionToString( values ) );

	      throw new DAOException( info.toString(), e );
	   }
   }

   /**
    * @param queryName
    * @param paramName
    * @param value
    * @return
    * @throws org.springframework.dao.DAOException
    */
   public List findByNamedQueryAndNamedParam( String queryName,
            											 String paramName,
            											 Object value ) throws DAOException {
      try {
         Validate.notNull( queryName, "A qyery string's name must not be null" );
         //CCR12710
  		if( logger.isDebugEnabled() ) {
              logger.debug( "GenericDAOImpl findByNamedQueryandNamedParam:" + queryName + " param: " + paramName + " " + value );
           }
         return getHibernateTemplate().findByNamedQueryAndNamedParam( queryName, paramName,
                  value );
	   }
	   catch( RuntimeException e ) {
	      StringBuffer info = new StringBuffer();
	      info.append( "Failed to execute a query by named query: " )
	      	 .append( queryName )
	      	 .append( " named param: " )
	      	 .append( paramName )
	      	 .append( " value: " )
	      	 .append( ToStringBuilder.reflectionToString( value ) );

	      throw new DAOException( info.toString(), e );
	   }
   }

   /** Execute a named query for persistent instances, binding a number of values to ":" named
    * parameters in the query string. A named query is defined in a Hibernate mapping file
    *
    * @param queryName the name of a Hibernate query in a mapping file
    * @param paramNames the names of the parameters
    * @param values the values of the parameters
    * @return a List containing 0 or more persistent instances
    * @throws org.springframework.dao.DAOException
    */
   public List findByNamedQueryAndNamedParam( String queryName,
            											 String[] paramNames,
            											 Object[] values ) throws DAOException {
      try {
         Validate.notNull( queryName, "A qyery string's name must not be null" );
         //CCR12710
  		if( logger.isDebugEnabled() ) {
              logger.debug( "GenericDAOImpl findByNamedParam[]:" + queryName + " param: " + paramNames + " " + values );
           }
         return this.getHibernateTemplate().findByNamedQueryAndNamedParam( queryName,
                  paramNames, values );
	   }
	   catch( RuntimeException e ) {
	      StringBuffer info = new StringBuffer();
	      info.append( "Failed to execute a query by named query: " )
	      	 .append( queryName )
	      	 .append( " named params: " )
	      	 .append( ToStringBuilder.reflectionToString( paramNames ) )
	      	 .append( " value: " )
	      	 .append( ToStringBuilder.reflectionToString( values ) );

	      throw new DAOException( info.toString(), e );
	   }
   }

   /**
    * @param queryName
    * @param valueBean
    * @return
    * @throws org.springframework.dao.DAOException
    */
   public List findByNamedQueryAndValueBean(String queryName, Object valueBean)
            throws DAOException {
      try {
         Validate.notNull( queryName, "A query string's name must not be null" );
         Validate.notNull( valueBean, "A value bean must not be null" );

         return this.getHibernateTemplate().findByNamedQueryAndValueBean( queryName, valueBean );
	   }
	   catch( RuntimeException e ) {
	      StringBuffer info = new StringBuffer();
	      info.append( "Failed to execute a query by named query: " )
	      	 .append( queryName )
	      	 .append( " value bean: " )
	      	 .append( ToStringBuilder.reflectionToString( valueBean ) );

	      throw new DAOException( info.toString(), e );
	   }
   }

   /**
    * @param queryString
    * @param valueBean
    * @return
    * @throws org.springframework.dao.DAOException
    */
   public List findByValueBean(String queryString, Object valueBean) throws DAOException {

      try {
         Validate.notNull( queryString, "A query string must not be null" );
         Validate.notNull( valueBean, "A value bean must not be null" );

         return this.getHibernateTemplate().findByValueBean( queryString, valueBean );
	   }
	   catch( RuntimeException e ) {
	      StringBuffer info = new StringBuffer();
	      info.append( "Failed to execute a query by query string: " )
	      	 .append( queryString )
	      	 .append( " value: " )
	      	 .append( ToStringBuilder.reflectionToString( valueBean ) );

	      throw new DAOException( info.toString(), e );
	   }
   }

   /**
    * @see gov.va.med.fw.persistent.DAOOperations#getQueryString(java.lang.String)
    */
   public String getQueryString(String queryName) throws DAOException {
	   Validate.notNull( queryName );

		Map contextData = new HashMap();
		contextData.put("queryName", queryName);
		HibernateCallback callback = new AbstractDAOAction(contextData) {
			public Object execute(Session session) {
				return session.getNamedQuery((String) getContextData().get("queryName")).getQueryString();
			}
		};

      try {
          return (String) this.getHibernateTemplate().execute(callback);
 	   }
 	   catch( Exception e ) {
 	      StringBuffer info = new StringBuffer();
 	      info.append( "Failed to get a query string by a given query name: " )
 	      	 .append( queryName);
 	      throw new DAOException( info.toString(), e );
 	   }
   }

   public Integer scroll(ScrollableCallback callback, QueryInfo query, QueryIncrementTracker tracker) {
		Validate.notNull( callback);
		Validate.notNull( query );

		ScrollableDAOAction action = new ScrollableDAOAction(callback, query, tracker);
		return (Integer) this.getHibernateTemplate().execute(action);
   }

   /**
    * @throws org.springframework.dao.DAOException
    */
   public void flush() throws DAOException {
      try {
         this.getHibernateTemplate().flush();
	   }
	   catch( RuntimeException e ) {
	      StringBuffer info = new StringBuffer();
	      info.append( "Failed to flush a session " );
	      throw new DAOException( info.toString(), e );
	   }
   }

   /**
    * @return
    */
   public String[] getFilterNames() {
      return this.getHibernateTemplate().getFilterNames();
   }

   /**
    * @return
    */
   public int getFlushMode() {
      return this.getHibernateTemplate().getFlushMode();
   }

   /**
    * @return
    */
   public SQLExceptionTranslator getJdbcExceptionTranslator() {
      return this.getHibernateTemplate().getJdbcExceptionTranslator();
   }

   /**
    * @return
    */
   public String getQueryCacheRegion() {
      return this.getHibernateTemplate().getQueryCacheRegion();
   }

   /**
    * @param proxy
    * @throws org.springframework.dao.DAOException
    */
   public void initialize(Object proxy) throws DAOException {
      try {
         this.getHibernateTemplate().initialize( proxy );
      }
      catch( RuntimeException e ) {
         throw new DAOException( "Failed to initialize object", e );
      }
   }

   /**
    * @return
    */
   public boolean isAllowCreate() {
      return this.getHibernateTemplate().isAllowCreate();
   }

   /**
    * @return
    */
   public boolean isAlwaysUseNewSession() {
      return this.getHibernateTemplate().isAlwaysUseNewSession();
   }

   /**
    * @return
    */
   public boolean isCacheQueries() {
      return this.getHibernateTemplate().isCacheQueries();
   }

   /**
    * @return
    */
   public boolean isCheckWriteOperations() {
      return this.getHibernateTemplate().isCheckWriteOperations();
   }

   /**
    * @return
    */
   public boolean isExposeNativeSession() {
      return this.getHibernateTemplate().isExposeNativeSession();
   }

   /**
    * @param queryString
    * @return
    * @throws org.springframework.dao.DAOException
    */
   public Iterator iterate(String queryString) throws DAOException {
      try {
         Validate.notNull( queryString, " A query string must not be null" );
         return this.getHibernateTemplate().iterate( queryString );
	   }
	   catch( RuntimeException e ) {
	      StringBuffer info = new StringBuffer();
	      info.append( "Failed to execute a query to return an iterator" );

	      throw new DAOException( info.toString(), e );
	   }
   }

   /**
    * @param queryString
    * @param value
    * @return
    * @throws org.springframework.dao.DAOException
    */
   public Iterator iterate(String queryString, Object value)
            throws DAOException {
      try {
         Validate.notNull( queryString, " A query string must not be null" );
         return this.getHibernateTemplate().iterate( queryString, value );
	   }
	   catch( RuntimeException e ) {
	      StringBuffer info = new StringBuffer();
	      info.append( "Failed to execute a query with value " )
	      	 .append( ToStringBuilder.reflectionToString( value ) )
	      	 .append( " to return an iterator" );

	      throw new DAOException( info.toString(), e );
	   }
   }

   /**
    * @param queryString
    * @param values
    * @return
    * @throws org.springframework.dao.DAOException
    */
   public Iterator iterate(String queryString, Object[] values)
            throws DAOException {
      try {
         Validate.notNull( queryString, " A query string must not be null" );
         return this.getHibernateTemplate().iterate( queryString, values );
	   }
	   catch( RuntimeException e ) {
	      StringBuffer info = new StringBuffer();
	      info.append( "Failed to execute a query with value " )
	      	 .append( ToStringBuilder.reflectionToString( values ) )
	      	 .append( " to return an iterator" );

	      throw new DAOException( info.toString(), e );
	   }
   }

   /**
    * @param entityClass
    * @param id
    * @return
    * @throws org.springframework.dao.DAOException
    */
   public Object load(Class entityClass, Serializable id)
            throws DAOException {
      try {
         Validate.notNull( entityClass, "An entity class must not be null" );
         Validate.notNull( id, "An entity ID must not be null" );

         return this.getHibernateTemplate().load( entityClass, id );
	   }
	   catch( RuntimeException e ) {
	      StringBuffer info = new StringBuffer();
	      info.append( "Failed to load an entity from a session: " )
	      	 .append( entityClass )
	      	 .append( " with id: " )
	      	 .append( ToStringBuilder.reflectionToString( id ) );

	      throw new DAOException( info.toString(), e );
	   }
   }

   /**
    * @param entity
    * @param id
    * @throws org.springframework.dao.DAOException
    */
   public void load(Object entity, Serializable id) throws DAOException {
      try {
         Validate.notNull( entity, "An entity must not be null" );
         Validate.notNull( id, "An entity ID must not be null" );

         this.getHibernateTemplate().load( entity, id );
	   }
	   catch( RuntimeException e ) {
	      StringBuffer info = new StringBuffer();
	      info.append( "Failed to load an entity from a session: " )
	      	 .append( entity )
	      	 .append( " with id: " )
	      	 .append( ToStringBuilder.reflectionToString( id ) );

	      throw new DAOException( info.toString(), e );
	   }
   }

   /**
    * @param entityName
    * @param id
    * @return
    * @throws org.springframework.dao.DAOException
    */
   public Object load(String entityName, Serializable id) throws DAOException {
      try {
         Validate.notNull( entityName, "An entity's name must not be null" );
         Validate.notNull( id, "An entity ID must not be null" );

         return this.getHibernateTemplate().load( entityName, id );
	   }
	   catch( RuntimeException e ) {
	      StringBuffer info = new StringBuffer();
	      info.append( "Failed to load from a session an entity named: " )
	      	 .append( entityName )
	      	 .append( " with id: " )
	      	 .append( ToStringBuilder.reflectionToString( id ) );

	      throw new DAOException( info.toString(), e );
	   }
   }

   /**
    * @param entityClass
    * @return
    * @throws org.springframework.dao.DAOException
    */
   public List loadAll(Class entityClass) throws DAOException {
      try {
         Validate.notNull( entityClass, "An entity's class must not be null" );

         return this.getHibernateTemplate().loadAll( entityClass );
	   }
	   catch( RuntimeException e ) {
	      StringBuffer info = new StringBuffer();
	      info.append( "Failed to load all instances of this class: " )
	      	 .append( ToStringBuilder.reflectionToString( entityClass ) );

	      throw new DAOException( info.toString(), e );
	   }
   }

   /**
    * @param entity
    * @return
    * @throws org.springframework.dao.DAOException
    */
   public Object merge(Object entity) throws DAOException {

      Object updated = entity;
      try {
         if( this.contains( entity ) ) {
            updated = this.getHibernateTemplate().merge( entity );

         }
	   }
	   catch( RuntimeException e ) {
	      StringBuffer info = new StringBuffer();
	      info.append( "Failed to merge an entity in memory to the persistent one: " )
	      	 .append( ToStringBuilder.reflectionToString( entity ) );

	      throw new DAOException( info.toString(), e );
	   }
	   return updated;
   }

   /**
    * @param entityName
    * @param entity
    * @return
    * @throws org.springframework.dao.DAOException
    */
   public Object merge(String entityName, Object entity) throws DAOException {
      try {
         Validate.notNull( entityName, "An entity's name must not be null" );
         Validate.notNull( entity, "An entity must not be null" );

         return this.getHibernateTemplate().merge( entityName, entity );
	   }
	   catch( RuntimeException e ) {
	      StringBuffer info = new StringBuffer();
	      info.append( "Failed to merge an entity named: " )
	      	 .append( entityName )
	      	 .append( " to the persistent one: " )
	      	 .append( ToStringBuilder.reflectionToString( entity ) );

	      throw new DAOException( info.toString(), e );
	   }
   }

   /**
    * @param entity
    * @throws org.springframework.dao.DAOException
    */
   public void refresh(Object entity) throws DAOException {
      try {
         if( this.getHibernateTemplate().contains( entity ) ) {
            this.getHibernateTemplate().refresh( entity );
         }
	   }
	   catch( RuntimeException e ) {
	      StringBuffer info = new StringBuffer();
	      info.append( "Refresh this entity with data from a database " )
	      	 .append( ToStringBuilder.reflectionToString( entity ) );

	      throw new DAOException( info.toString(), e );
	   }
   }

   /**
    * @param allowCreate
    */
   public void setAllowCreate(boolean allowCreate) {
      this.getHibernateTemplate().setAllowCreate( allowCreate );
   }

   /**
    * @param alwaysUseNewSession
    */
   public void setAlwaysUseNewSession(boolean alwaysUseNewSession) {
      this.getHibernateTemplate().setAlwaysUseNewSession( alwaysUseNewSession );
   }

   /**
    * @param beanFactory
    * @throws org.springframework.beans.BeansException
    */
   public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
      this.getHibernateTemplate().setBeanFactory( beanFactory );
   }

   /**
    * @param cacheQueries
    */
   public void setCacheQueries(boolean cacheQueries) {
      this.getHibernateTemplate().setCacheQueries( cacheQueries );
   }

   /**
    * @param checkWriteOperations
    */
   public void setCheckWriteOperations(boolean checkWriteOperations) {
      this.getHibernateTemplate().setCheckWriteOperations( checkWriteOperations );
   }

   /**
    * @param entityInterceptorBeanName
    */
   public void setEntityInterceptorBeanName(String entityInterceptorBeanName) {
      this.getHibernateTemplate().setEntityInterceptorBeanName( entityInterceptorBeanName );
   }

   /**
    * @param exposeNativeSession
    */
   public void setExposeNativeSession(boolean exposeNativeSession) {
      this.getHibernateTemplate().setExposeNativeSession( exposeNativeSession );
   }

   /**
    * @param filter
    */
   public void setFilterName(String filter) {
      this.getHibernateTemplate().setFilterName( filter );
   }

   /**
    * @param filterNames
    */
   public void setFilterNames(String[] filterNames) {
      this.getHibernateTemplate().setFilterNames( filterNames );
   }

   /**
    * @param flushMode
    */
   public void setFlushMode(int flushMode) {
      this.getHibernateTemplate().setFlushMode( flushMode );
   }

   /**
    * @param constantName
    */
   public void setFlushModeName(String constantName) {
      this.getHibernateTemplate().setFlushModeName( constantName );
   }

   /**
    * @param jdbcExceptionTranslator
    */
   public void setJdbcExceptionTranslator(
            SQLExceptionTranslator jdbcExceptionTranslator) {
      this.getHibernateTemplate().setJdbcExceptionTranslator( jdbcExceptionTranslator );
   }

   /**
    * @param queryCacheRegion
    */
   public void setQueryCacheRegion(String queryCacheRegion) {
      this.getHibernateTemplate().setQueryCacheRegion( queryCacheRegion );
   }

	/** (non-Javadoc)
	 * @see gov.va.med.fw.persistent.DAOOperations#update(java.lang.Object)
	 */
	public void update(Object entity) throws DAOException {
		saveObject(entity);
	}

	public List find(String query, int firstRecord, int maxRecord, int fetchSize) throws DAOException {
		Validate.notNull(query);

		Map contextData = new HashMap();
		contextData.put("query", query);
		contextData.put("firstRecord", new Integer(firstRecord));
		contextData.put("maxRecord", new Integer(maxRecord));
		contextData.put("fetchSize", new Integer(fetchSize));
		HibernateCallback callback = new AbstractDAOAction(contextData) {
			public Object execute(Session session) {
				return session.getNamedQuery((String) getContextData().get("query")).
					setFirstResult(((Integer) getContextData().get("firstRecord")).intValue()).
					setMaxResults(((Integer) getContextData().get("maxRecord")).intValue()).
					setFetchSize(((Integer) getContextData().get("fetchSize")).intValue()).
					list();
			}
		};

		return this.getHibernateTemplate().executeFind(callback);
	}

	public List find(String query, Object[] params, int firstRecord, int maxRecord, int fetchSize) throws DAOException {
		Map contextData = new HashMap();
		contextData.put("query", query);
		contextData.put("params", params);
		contextData.put("firstRecord", new Integer(firstRecord));
		contextData.put("maxRecord", new Integer(maxRecord));
		contextData.put("fetchSize", new Integer(fetchSize));
		HibernateCallback callback = new AbstractDAOAction(contextData) {
			public Object execute(Session session) {
				Query queryImpl = session.getNamedQuery((String) getContextData().get("query")).
					setFirstResult(((Integer) getContextData().get("firstRecord")).intValue()).
					setMaxResults(((Integer) getContextData().get("maxRecord")).intValue()).
					setFetchSize(((Integer) getContextData().get("fetchSize")).intValue());

				String[] paramNames = queryImpl.getNamedParameters();
				Object[] targetParams = (Object[]) getContextData().get("params");
				if(targetParams != null && targetParams.length > 0) {
					for(int i=0; paramNames != null && i < paramNames.length; i++) {
						queryImpl.setParameter(paramNames[i], targetParams[i]);
					}
				}
				return queryImpl.list();
			}
		};

		return this.getHibernateTemplate().executeFind(callback);
	}

    public List find(String query, String[] paramNames, Object[] params, int firstRecord, int maxRecord, int fetchSize) throws DAOException {
		Map contextData = new HashMap();
		contextData.put("query", query);
		contextData.put("paramNames", paramNames);
		contextData.put("params", params);
		contextData.put("firstRecord", new Integer(firstRecord));
		contextData.put("maxRecord", new Integer(maxRecord));
		contextData.put("fetchSize", new Integer(fetchSize));
		HibernateCallback callback = new AbstractDAOAction(contextData) {
			public Object execute(Session session) {
				Query queryImpl = session.getNamedQuery((String) getContextData().get("query")).
					setFirstResult(((Integer) getContextData().get("firstRecord")).intValue()).
					setMaxResults(((Integer) getContextData().get("maxRecord")).intValue()).
					setFetchSize(((Integer) getContextData().get("fetchSize")).intValue());

				String[] targetParamNames = (String[]) getContextData().get("paramNames");
				Object[] targetParams = (Object[]) getContextData().get("params");
		        if(targetParamNames != null) {
			          for (int i = 0; i < targetParamNames.length; i++) {
			              queryImpl.setParameter(targetParamNames[i], targetParams[i]);
			          }
		        }

			        return queryImpl.list();
			}
		};
		return this.getHibernateTemplate().executeFind(callback);
    }


	/* (non-Javadoc)
	 * @see gov.va.med.fw.persistent.DAOOperations#bulkUpdate(java.lang.String)
	 */
	public int bulkUpdate(String query) throws DAOException {
		try {
			return getHibernateTemplate().bulkUpdate(query);
		} catch(Exception e) {
			throw new DAOException("Unable to perform bulk update", e);
		}
	}

	/* (non-Javadoc)
	 * @see gov.va.med.fw.persistent.DAOOperations#bulkUpdate(java.lang.String, java.lang.Object[])
	 */
	public int bulkUpdate(String query, Object[] parameters) throws DAOException {
		try {
			return getHibernateTemplate().bulkUpdate(query, parameters);
		} catch(Exception e) {
			throw new DAOException("Unable to perform bulk update", e);
		}
	}

	protected Throwable getRootExceptionOfType(Throwable e, Class targetRootExceptionType) {
		if(e != null && targetRootExceptionType != null) {
			Throwable rootCause = ExceptionUtils.getRootCause(e);
			if(rootCause != null && targetRootExceptionType.isAssignableFrom(rootCause.getClass()))
				return rootCause;
		}
		return null;
	}

    public List executeSQLQuery(final String sqlQuery) throws DAOException {
        List results = null;
        try {
            HibernateCallback callback = new AbstractDAOAction() {
                public Object execute(Session session) {
                    Query hibSqlQuery = session.createSQLQuery(sqlQuery);
                    return hibSqlQuery.list();
                }
            };
            results = this.getHibernateTemplate().executeFind(callback);
        }
        catch (DataAccessException e) {
        	if (logger.isErrorEnabled())
            {
                logger.error("Failed to execute an sql query due to error " + e);//sqlQuery
            }
            throw new DAOException("Failed to execute an sql query:", e);
        }

        return results;
    }

    public List executeSQLQuery(final String sqlQuery, final String prepareStmt) throws DAOException {
        List results = new ArrayList();
        try {
            HibernateCallback callback = new AbstractDAOAction() {
                public Object execute(Session session) throws DAOException {
                	if (prepareStmt != null && prepareStmt.length() > 0) {
						try {
							PreparedStatement ps = session.connection()
									.prepareStatement(prepareStmt);
							ps.execute();
						} catch (SQLException ex) {
							throw new DAOException(
									"Failed to execute prepare statement: " + prepareStmt ,
									ex);
						}
					}
                    Query hibSqlQuery = session.createSQLQuery(sqlQuery);
                    return hibSqlQuery.list();
                }
            };
            results = this.getHibernateTemplate().executeFind(callback);
        }
        catch (DataAccessException e) {
            throw new DAOException("Failed to execute an sql query:" + sqlQuery, e);
        }

        return results;
    }
}