Open Source Repository

Home /hibernate/hibernate-3.2.6.ga | Repository Home


org/hibernate/impl/AbstractQueryImpl.java
//$Id: AbstractQueryImpl.java 10855 2006-11-21 21:57:32Z [email protected] $
package org.hibernate.impl;

import java.io.Serializable;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;

import org.hibernate.CacheMode;
import org.hibernate.FlushMode;
import org.hibernate.Hibernate;
import org.hibernate.HibernateException;
import org.hibernate.MappingException;
import org.hibernate.NonUniqueResultException;
import org.hibernate.PropertyNotFoundException;
import org.hibernate.Query;
import org.hibernate.QueryException;
import org.hibernate.engine.QueryParameters;
import org.hibernate.engine.RowSelection;
import org.hibernate.engine.SessionImplementor;
import org.hibernate.engine.TypedValue;
import org.hibernate.engine.query.ParameterMetadata;
import org.hibernate.hql.classic.ParserHelper;
import org.hibernate.property.Getter;
import org.hibernate.proxy.HibernateProxyHelper;
import org.hibernate.transform.ResultTransformer;
import org.hibernate.type.SerializableType;
import org.hibernate.type.Type;
import org.hibernate.type.TypeFactory;
import org.hibernate.util.ArrayHelper;
import org.hibernate.util.MarkerObject;
import org.hibernate.util.ReflectHelper;
import org.hibernate.util.StringHelper;

/**
 * Abstract implementation of the Query interface.
 *
 @author Gavin King, Max Andersen
 */
public abstract class AbstractQueryImpl implements Query {

  private static final Object UNSET_PARAMETER = new MarkerObject("<unset parameter>");
  private static final Object UNSET_TYPE = new MarkerObject("<unset type>");

  private final String queryString;
  protected final SessionImplementor session;
  protected final ParameterMetadata parameterMetadata;

  // parameter bind values...
  private List values = new ArrayList(4);
  private List types = new ArrayList(4);
  private Map namedParameters = new HashMap(4);
  private Map namedParameterLists = new HashMap(4);

  private Object optionalObject;
  private Serializable optionalId;
  private String optionalEntityName;

  private RowSelection selection;
  private boolean cacheable;
  private String cacheRegion;
  private String comment;
  private FlushMode flushMode;
  private CacheMode cacheMode;
  private FlushMode sessionFlushMode;
  private CacheMode sessionCacheMode;
  private Serializable collectionKey;
  private boolean readOnly;
  private ResultTransformer resultTransformer;

  public AbstractQueryImpl(
      String queryString,
          FlushMode flushMode,
          SessionImplementor session,
          ParameterMetadata parameterMetadata) {
    this.session = session;
    this.queryString = queryString;
    this.selection = new RowSelection();
    this.flushMode = flushMode;
    this.cacheMode = null;
    this.parameterMetadata = parameterMetadata;
  }

  public String toString() {
    return StringHelper.unqualifygetClass().getName() ) '(' + queryString + ')';
  }

  public final String getQueryString() {
    return queryString;
  }

  //TODO: maybe call it getRowSelection() ?
  public RowSelection getSelection() {
    return selection;
  }
  
  public Query setFlushMode(FlushMode flushMode) {
    this.flushMode = flushMode;
    return this;
  }
  
  public Query setCacheMode(CacheMode cacheMode) {
    this.cacheMode = cacheMode;
    return this;
  }

  public Query setCacheable(boolean cacheable) {
    this.cacheable = cacheable;
    return this;
  }

  public Query setCacheRegion(String cacheRegion) {
    if (cacheRegion != null)
      this.cacheRegion = cacheRegion.trim();
    return this;
  }

  public Query setComment(String comment) {
    this.comment = comment;
    return this;
  }

  public Query setFirstResult(int firstResult) {
    selection.setFirstRownew Integer(firstResult) );
    return this;
  }

  public Query setMaxResults(int maxResults) {
    selection.setMaxRowsnew Integer(maxResults) );
    return this;
  }

  public Query setTimeout(int timeout) {
    selection.setTimeoutnew Integer(timeout) );
    return this;
  }
  public Query setFetchSize(int fetchSize) {
    selection.setFetchSizenew Integer(fetchSize) );
    return this;
  }

  public Type[] getReturnTypes() throws HibernateException {
    return session.getFactory().getReturnTypesqueryString );
  }

  public String[] getReturnAliases() throws HibernateException {
    return session.getFactory().getReturnAliasesqueryString );
  }

  public Query setCollectionKey(Serializable collectionKey) {
    this.collectionKey = collectionKey;
    return this;
  }

  public boolean isReadOnly() {
    return readOnly;
  }

  public Query setReadOnly(boolean readOnly) {
    this.readOnly = readOnly;
    return this;
  }

  public Query setResultTransformer(ResultTransformer transformer) {
    this.resultTransformer = transformer;
    return this;
  }
  
  public void setOptionalEntityName(String optionalEntityName) {
    this.optionalEntityName = optionalEntityName;
  }

  public void setOptionalId(Serializable optionalId) {
    this.optionalId = optionalId;
  }

  public void setOptionalObject(Object optionalObject) {
    this.optionalObject = optionalObject;
  }

  SessionImplementor getSession() {
    return session;
  }

  protected abstract Map getLockModes();


  // Parameter handling code ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

  /**
   * Returns a shallow copy of the named parameter value map.
   *
   @return Shallow copy of the named parameter value map
   */
  protected Map getNamedParams() {
    return new HashMapnamedParameters );
  }

  /**
   * Returns an array representing all named parameter names encountered
   * during (intial) parsing of the query.
   <p/>
   * Note <i>initial</i> here means different things depending on whether
   * this is a native-sql query or an HQL/filter query.  For native-sql, a
   * precursory inspection of the query string is performed specifically to
   * locate defined parameters.  For HQL/filter queries, this is the
   * information returned from the query-translator.  This distinction
   * holds true for all parameter metadata exposed here.
   *
   @return Array of named parameter names.
   @throws HibernateException
   */
  public String[] getNamedParameters() throws HibernateException {
    return ArrayHelper.toStringArrayparameterMetadata.getNamedParameterNames() );
  }

  /**
   * Does this query contain named parameters?
   *
   @return True if the query was found to contain named parameters; false
   * otherwise;
   */
  public boolean hasNamedParameters() {
    return parameterMetadata.getNamedParameterNames().size() 0;
  }

  /**
   * Retreive the value map for any named parameter lists (i.e., for
   * auto-expansion) bound to this query.
   *
   @return The parameter list value map.
   */
  protected Map getNamedParameterLists() {
    return namedParameterLists;
  }

  /**
   * Retreives the list of parameter values bound to this query for
   * ordinal parameters.
   *
   @return The ordinal parameter values.
   */
  protected List getValues() {
    return values;
  }

  /**
   * Retreives the list of parameter {@link Type type}s bound to this query for
   * ordinal parameters.
   *
   @return The ordinal parameter types.
   */
  protected List getTypes() {
    return types;
  }

  /**
   * Perform parameter validation.  Used prior to executing the encapsulated
   * query.
   *
   @throws QueryException
   */
  protected void verifyParameters() throws QueryException {
    verifyParameters(false);
  }

  /**
   * Perform parameter validation.  Used prior to executing the encapsulated
   * query.
   *
   @param reserveFirstParameter if true, the first ? will not be verified since
   * its needed for e.g. callable statements returning a out parameter
   @throws HibernateException
   */
  protected void verifyParameters(boolean reserveFirstParameterthrows HibernateException {
    if parameterMetadata.getNamedParameterNames().size() != namedParameters.size() + namedParameterLists.size() ) {
      Set missingParams = new HashSetparameterMetadata.getNamedParameterNames() );
      missingParams.removeAllnamedParameterLists.keySet() );
      missingParams.removeAllnamedParameters.keySet() );
      throw new QueryException"Not all named parameters have been set: " + missingParams, getQueryString() );
    }

    int positionalValueSpan = 0;
    for int i = 0; i < values.size(); i++ ) {
      Object object = types.get);
      ifvalues.get== UNSET_PARAMETER || object == UNSET_TYPE ) {
        if reserveFirstParameter && i==) {
          continue;
        }
        else {
          throw new QueryException"Unset positional parameter at position: " + i, getQueryString() );
        }
      }
      positionalValueSpan += ( (Typeobject ).getColumnSpansession.getFactory() );
    }

    if parameterMetadata.getOrdinalParameterCount() != positionalValueSpan ) {
      if reserveFirstParameter && parameterMetadata.getOrdinalParameterCount() != positionalValueSpan ) {
        throw new QueryException(
             "Expected positional parameter count: " +
             (parameterMetadata.getOrdinalParameterCount()-1+
             ", actual parameters: " +
             values,
             getQueryString()
           );
      }
      else if !reserveFirstParameter ) {
        throw new QueryException(
             "Expected positional parameter count: " +
             parameterMetadata.getOrdinalParameterCount() +
             ", actual parameters: " +
             values,
             getQueryString()
           );
      }
    }
  }

  public Query setParameter(int position, Object val, Type type) {
    if parameterMetadata.getOrdinalParameterCount() == ) {
      throw new IllegalArgumentException("No positional parameters in query: " + getQueryString() );
    }
    if position < || position > parameterMetadata.getOrdinalParameterCount() ) {
      throw new IllegalArgumentException("Positional parameter does not exist: " + position + " in query: " + getQueryString() );
    }
    int size = values.size();
    if position < size ) {
      values.setposition, val );
      types.setposition, type );
    }
    else {
      // prepend value and type list with null for any positions before the wanted position.
      for int i = 0; i < position - size; i++ ) {
        values.addUNSET_PARAMETER );
        types.addUNSET_TYPE );
      }
      values.addval );
      types.addtype );
    }
    return this;
  }

  public Query setParameter(String name, Object val, Type type) {
    if !parameterMetadata.getNamedParameterNames().containsname ) ) {
      throw new IllegalArgumentException("Parameter " + name + " does not exist as a named parameter in [" + getQueryString() "]");
    }
    else {
       namedParameters.putname, new TypedValuetype, val, session.getEntityMode() ) );
       return this;
    }
  }

  public Query setParameter(int position, Object valthrows HibernateException {
    if (val == null) {
      setParameterposition, val, Hibernate.SERIALIZABLE );
    }
    else {
      setParameterposition, val, determineTypeposition, val ) );
    }
    return this;
  }

  public Query setParameter(String name, Object valthrows HibernateException {
    if (val == null) {
      Type type = parameterMetadata.getNamedParameterExpectedTypename );
      if type == null ) {
        type = Hibernate.SERIALIZABLE;
      }
      setParametername, val, type );
    }
    else {
      setParametername, val, determineTypename, val ) );
    }
    return this;
  }

  protected Type determineType(int paramPosition, Object paramValue, Type defaultType) {
    Type type = parameterMetadata.getOrdinalParameterExpectedTypeparamPosition + );
    if type == null ) {
      type = defaultType;
    }
    return type;
  }

  protected Type determineType(int paramPosition, Object paramValuethrows HibernateException {
    Type type = parameterMetadata.getOrdinalParameterExpectedTypeparamPosition + );
    if type == null ) {
      type = guessTypeparamValue );
    }
    return type;
  }

  protected Type determineType(String paramName, Object paramValue, Type defaultType) {
    Type type = parameterMetadata.getNamedParameterExpectedTypeparamName );
    if type == null ) {
      type = defaultType;
    }
    return type;
  }

  protected Type determineType(String paramName, Object paramValuethrows HibernateException {
    Type type = parameterMetadata.getNamedParameterExpectedTypeparamName );
    if type == null ) {
      type = guessTypeparamValue );
    }
    return type;
  }

  protected Type determineType(String paramName, Class clazzthrows HibernateException {
    Type type = parameterMetadata.getNamedParameterExpectedTypeparamName );
    if type == null ) {
      type = guessTypeclazz );
    }
    return type;
  }

  private Type guessType(Object paramthrows HibernateException {
    Class clazz = HibernateProxyHelper.getClassWithoutInitializingProxyparam );
    return guessTypeclazz );
  }

  private Type guessType(Class clazzthrows HibernateException {
    String typename = clazz.getName();
    Type type = TypeFactory.heuristicType(typename);
    boolean serializable = type!=null && type instanceof SerializableType;
    if (type==null || serializable) {
      try {
        session.getFactory().getEntityPersisterclazz.getName() );
      }
      catch (MappingException me) {
        if (serializable) {
          return type;
        }
        else {
          throw new HibernateException("Could not determine a type for class: " + typename);
        }
      }
      return Hibernate.entity(clazz);
    }
    else {
      return type;
    }
  }

  public Query setString(int position, String val) {
    setParameter(position, val, Hibernate.STRING);
    return this;
  }

  public Query setCharacter(int position, char val) {
    setParameter(position, new Character(val), Hibernate.CHARACTER);
    return this;
  }

  public Query setBoolean(int position, boolean val) {
    Boolean valueToUse = val ? Boolean.TRUE : Boolean.FALSE;
    Type typeToUse = determineTypeposition, valueToUse, Hibernate.BOOLEAN );
    setParameterposition, valueToUse, typeToUse );
    return this;
  }

  public Query setByte(int position, byte val) {
    setParameter(position, new Byte(val), Hibernate.BYTE);
    return this;
  }

  public Query setShort(int position, short val) {
    setParameter(position, new Short(val), Hibernate.SHORT);
    return this;
  }

  public Query setInteger(int position, int val) {
    setParameter(position, new Integer(val), Hibernate.INTEGER);
    return this;
  }

  public Query setLong(int position, long val) {
    setParameter(position, new Long(val), Hibernate.LONG);
    return this;
  }

  public Query setFloat(int position, float val) {
    setParameter(position, new Float(val), Hibernate.FLOAT);
    return this;
  }

  public Query setDouble(int position, double val) {
    setParameter(position, new Double(val), Hibernate.DOUBLE);
    return this;
  }

  public Query setBinary(int position, byte[] val) {
    setParameter(position, val, Hibernate.BINARY);
    return this;
  }

  public Query setText(int position, String val) {
    setParameter(position, val, Hibernate.TEXT);
    return this;
  }

  public Query setSerializable(int position, Serializable val) {
    setParameter(position, val, Hibernate.SERIALIZABLE);
    return this;
  }

  public Query setDate(int position, Date date) {
    setParameter(position, date, Hibernate.DATE);
    return this;
  }

  public Query setTime(int position, Date date) {
    setParameter(position, date, Hibernate.TIME);
    return this;
  }

  public Query setTimestamp(int position, Date date) {
    setParameter(position, date, Hibernate.TIMESTAMP);
    return this;
  }

  public Query setEntity(int position, Object val) {
    setParameterposition, val, Hibernate.entityresolveEntityNameval ) ) );
    return this;
  }

  private String resolveEntityName(Object val) {
    if val == null ) {
      throw new IllegalArgumentException"entity for parameter binding cannot be null" );
    }
    return session.bestGuessEntityNameval );
  }

  public Query setLocale(int position, Locale locale) {
    setParameter(position, locale, Hibernate.LOCALE);
    return this;
  }

  public Query setCalendar(int position, Calendar calendar) {
    setParameter(position, calendar, Hibernate.CALENDAR);
    return this;
  }

  public Query setCalendarDate(int position, Calendar calendar) {
    setParameter(position, calendar, Hibernate.CALENDAR_DATE);
    return this;
  }

  public Query setBinary(String name, byte[] val) {
    setParameter(name, val, Hibernate.BINARY);
    return this;
  }

  public Query setText(String name, String val) {
    setParameter(name, val, Hibernate.TEXT);
    return this;
  }

  public Query setBoolean(String name, boolean val) {
    Boolean valueToUse = val ? Boolean.TRUE : Boolean.FALSE;
    Type typeToUse = determineTypename, valueToUse, Hibernate.BOOLEAN );
    setParametername, valueToUse, typeToUse );
    return this;
  }

  public Query setByte(String name, byte val) {
    setParameter(name, new Byte(val), Hibernate.BYTE);
    return this;
  }

  public Query setCharacter(String name, char val) {
    setParameter(name, new Character(val), Hibernate.CHARACTER);
    return this;
  }

  public Query setDate(String name, Date date) {
    setParameter(name, date, Hibernate.DATE);
    return this;
  }

  public Query setDouble(String name, double val) {
    setParameter(name, new Double(val), Hibernate.DOUBLE);
    return this;
  }

  public Query setEntity(String name, Object val) {
    setParametername, val, Hibernate.entityresolveEntityNameval ) ) );
    return this;
  }

  public Query setFloat(String name, float val) {
    setParameter(name, new Float(val), Hibernate.FLOAT);
    return this;
  }

  public Query setInteger(String name, int val) {
    setParameter(name, new Integer(val), Hibernate.INTEGER);
    return this;
  }

  public Query setLocale(String name, Locale locale) {
    setParameter(name, locale, Hibernate.LOCALE);
    return this;
  }

  public Query setCalendar(String name, Calendar calendar) {
    setParameter(name, calendar, Hibernate.CALENDAR);
    return this;
  }

  public Query setCalendarDate(String name, Calendar calendar) {
    setParameter(name, calendar, Hibernate.CALENDAR_DATE);
    return this;
  }

  public Query setLong(String name, long val) {
    setParameter(name, new Long(val), Hibernate.LONG);
    return this;
  }

  public Query setSerializable(String name, Serializable val) {
    setParameter(name, val, Hibernate.SERIALIZABLE);
    return this;
  }

  public Query setShort(String name, short val) {
    setParameter(name, new Short(val), Hibernate.SHORT);
    return this;
  }

  public Query setString(String name, String val) {
    setParameter(name, val, Hibernate.STRING);
    return this;
  }

  public Query setTime(String name, Date date) {
    setParameter(name, date, Hibernate.TIME);
    return this;
  }

  public Query setTimestamp(String name, Date date) {
    setParameter(name, date, Hibernate.TIMESTAMP);
    return this;
  }

  public Query setBigDecimal(int position, BigDecimal number) {
    setParameter(position, number, Hibernate.BIG_DECIMAL);
    return this;
  }

  public Query setBigDecimal(String name, BigDecimal number) {
    setParameter(name, number, Hibernate.BIG_DECIMAL);
    return this;
  }

  public Query setBigInteger(int position, BigInteger number) {
    setParameter(position, number, Hibernate.BIG_INTEGER);
    return this;
  }

  public Query setBigInteger(String name, BigInteger number) {
    setParameter(name, number, Hibernate.BIG_INTEGER);
    return this;
  }

  public Query setParameterList(String name, Collection vals, Type typethrows HibernateException {
    if !parameterMetadata.getNamedParameterNames().containsname ) ) {
      throw new IllegalArgumentException("Parameter " + name + " does not exist as a named parameter in [" + getQueryString() "]");
    }
    namedParameterLists.putname, new TypedValuetype, vals, session.getEntityMode() ) );
    return this;
  }
  
  /**
   * Warning: adds new parameters to the argument by side-effect, as well as
   * mutating the query string!
   */
  protected String expandParameterLists(Map namedParamsCopy) {
    String query = this.queryString;
    Iterator iter = namedParameterLists.entrySet().iterator();
    while iter.hasNext() ) {
      Map.Entry me = (Map.Entryiter.next();
      query = expandParameterListquery, (Stringme.getKey()(TypedValueme.getValue(), namedParamsCopy );
    }
    return query;
  }

  /**
   * Warning: adds new parameters to the argument by side-effect, as well as
   * mutating the query string!
   */
  private String expandParameterList(String query, String name, TypedValue typedList, Map namedParamsCopy) {
    Collection vals = (CollectiontypedList.getValue();
    Type type = typedList.getType();
    if vals.size() == ) {
      // short-circuit for performance...
      namedParamsCopy.putname, new TypedValuetype, vals.iterator().next(), session.getEntityMode() ) );
      return query;
    }

    StringBuffer list = new StringBuffer16 );
    Iterator iter = vals.iterator();
    int i = 0;
    boolean isJpaPositionalParam = parameterMetadata.getNamedParameterDescriptorname ).isJpaStyle();
    while iter.hasNext() ) {
      String alias = isJpaPositionalParam ? 'x' + name : name + i++ + '_';
      namedParamsCopy.putalias, new TypedValuetype, iter.next(), session.getEntityMode() ) );
      list.appendParserHelper.HQL_VARIABLE_PREFIX ).appendalias );
      if iter.hasNext() ) {
        list.append", " );
      }
    }
    String paramPrefix = isJpaPositionalParam ? "?" : ParserHelper.HQL_VARIABLE_PREFIX;
    return StringHelper.replacequery, paramPrefix + name, list.toString()true );
  }

  public Query setParameterList(String name, Collection valsthrows HibernateException {
    if vals == null ) {
      throw new QueryException"Collection must be not null!" );
    }

    ifvals.size() == ) {
      setParameterListname, vals, null );
    }
    else {
      setParameterList(name, vals, determineTypename, vals.iterator().next() ) );
    }

    return this;
  }

  public Query setParameterList(String name, Object[] vals, Type typethrows HibernateException {
    return setParameterListname, Arrays.asList(vals), type );
  }

  public Query setParameterList(String name, Object[] valsthrows HibernateException {
    return setParameterListname, Arrays.asList(vals) );
  }

  public Query setProperties(Map mapthrows HibernateException {
    String[] params = getNamedParameters();
    for (int i = 0; i < params.length; i++) {
      String namedParam = params[i];
        final Object object = map.get(namedParam);
        if(object==null) {
          continue;
        }
        Class retType = object.getClass();
        if Collection.class.isAssignableFromretType ) ) {
          setParameterListnamedParam, Collection object );
        }
        else if retType.isArray() ) {
          setParameterListnamedParam, Object[] ) object );
        }
        else {
          setParameternamedParam, object, determineTypenamedParam, retType ) );
        }

      
    }
    return this;        
  }

  public Query setProperties(Object beanthrows HibernateException {
    Class clazz = bean.getClass();
    String[] params = getNamedParameters();
    for (int i = 0; i < params.length; i++) {
      String namedParam = params[i];
      try {
        Getter getter = ReflectHelper.getGetterclazz, namedParam );
        Class retType = getter.getReturnType();
        final Object object = getter.getbean );
        if Collection.class.isAssignableFromretType ) ) {
          setParameterListnamedParam, Collection object );
        }
        else if retType.isArray() ) {
           setParameterListnamedParam, Object[] ) object );
        }
        else {
          setParameternamedParam, object, determineTypenamedParam, retType ) );
        }
      }
      catch (PropertyNotFoundException pnfe) {
        // ignore
      }
    }
    return this;
  }

  public Query setParameters(Object[] values, Type[] types) {
    this.values = Arrays.asList(values);
    this.types = Arrays.asList(types);
    return this;
  }


  // Execution methods ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

  public Object uniqueResult() throws HibernateException {
    return uniqueElementlist() );
  }

  static Object uniqueElement(List listthrows NonUniqueResultException {
    int size = list.size();
    if (size==0return null;
    Object first = list.get(0);
    for int i=1; i<size; i++ ) {
      if list.get(i)!=first ) {
        throw new NonUniqueResultExceptionlist.size() );
      }
    }
    return first;
  }

  protected RowSelection getRowSelection() {
    return selection;
  }

  public Type[] typeArray() {
    return ArrayHelper.toTypeArraygetTypes() );
  }
  
  public Object[] valueArray() {
    return getValues().toArray();
  }

  public QueryParameters getQueryParameters(Map namedParams) {
    return new QueryParameters(
        typeArray(),
        valueArray(),
        namedParams,
        getLockModes(),
        getSelection(),
        readOnly,
        cacheable,
        cacheRegion,
        comment,
        collectionKey == null null new Serializable[] { collectionKey },
        optionalObject,
        optionalEntityName,
        optionalId,
        resultTransformer
    );
  }
  
  protected void before() {
    if flushMode!=null ) {
      sessionFlushMode = getSession().getFlushMode();
      getSession().setFlushMode(flushMode);
    }
    if cacheMode!=null ) {
      sessionCacheMode = getSession().getCacheMode();
      getSession().setCacheMode(cacheMode);
    }
  }
  
  protected void after() {
    if (sessionFlushMode!=null) {
      getSession().setFlushMode(sessionFlushMode);
      sessionFlushMode = null;
    }
    if (sessionCacheMode!=null) {
      getSession().setCacheMode(sessionCacheMode);
      sessionCacheMode = null;
    }
  }
}