org.hibernate.engine.spi.QueryParameters.java Source code

Java tutorial

Introduction

Here is the source code for org.hibernate.engine.spi.QueryParameters.java

Source

/*
 * Hibernate, Relational Persistence for Idiomatic Java
 *
 * License: GNU Lesser General Public License (LGPL), version 2.1 or later.
 * See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
 */
package org.hibernate.engine.spi;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;

import org.hibernate.HibernateException;
import org.hibernate.LockOptions;
import org.hibernate.QueryException;
import org.hibernate.ScrollMode;
import org.hibernate.engine.query.spi.HQLQueryPlan;
import org.hibernate.hql.internal.classic.ParserHelper;
import org.hibernate.internal.CoreLogging;
import org.hibernate.internal.FilterImpl;
import org.hibernate.internal.util.EntityPrinter;
import org.hibernate.internal.util.collections.ArrayHelper;
import org.hibernate.query.internal.QueryParameterBindingsImpl;
import org.hibernate.query.spi.QueryParameterBindings;
import org.hibernate.transform.ResultTransformer;
import org.hibernate.type.ComponentType;
import org.hibernate.type.Type;

import org.jboss.logging.Logger;

/**
 * @author Gavin King
 */
public final class QueryParameters {
    private static final Logger LOG = CoreLogging.logger(QueryParameters.class);

    /**
     * Symbols used to split SQL string into tokens in {@link #processFilters(String, Map, SessionFactoryImplementor)}.
     */
    private static final String SYMBOLS = ParserHelper.HQL_SEPARATORS.replace("'", "");

    private Type[] positionalParameterTypes;
    private Object[] positionalParameterValues;
    private Map<String, TypedValue> namedParameters;

    private LockOptions lockOptions;
    private RowSelection rowSelection;
    private boolean cacheable;
    private String cacheRegion;
    private String comment;
    private List<String> queryHints;
    private ScrollMode scrollMode;
    private Serializable[] collectionKeys;
    private Object optionalObject;
    private String optionalEntityName;
    private Serializable optionalId;
    private boolean isReadOnlyInitialized;
    private boolean readOnly;
    private boolean callable;
    private boolean autodiscovertypes;
    private boolean isNaturalKeyLookup;
    private boolean passDistinctThrough = true;

    private final ResultTransformer resultTransformer; // why is all others non final ?

    private String processedSQL;
    private Type[] processedPositionalParameterTypes;
    private Object[] processedPositionalParameterValues;

    private HQLQueryPlan queryPlan;

    public QueryParameters() {
        this(ArrayHelper.EMPTY_TYPE_ARRAY, ArrayHelper.EMPTY_OBJECT_ARRAY);
    }

    public QueryParameters(Type type, Object value) {
        this(new Type[] { type }, new Object[] { value });
    }

    public QueryParameters(final Type[] positionalParameterTypes, final Object[] positionalParameterValues,
            final Object optionalObject, final String optionalEntityName, final Serializable optionalObjectId) {
        this(positionalParameterTypes, positionalParameterValues);
        this.optionalObject = optionalObject;
        this.optionalId = optionalObjectId;
        this.optionalEntityName = optionalEntityName;
    }

    public QueryParameters(final Type[] positionalParameterTypes, final Object[] positionalParameterValues) {
        this(positionalParameterTypes, positionalParameterValues, null, null, false, false, false, null, null, null,
                false, null);
    }

    public QueryParameters(final Type[] positionalParameterTypes, final Object[] positionalParameterValues,
            final Serializable[] collectionKeys) {
        this(positionalParameterTypes, positionalParameterValues, null, collectionKeys);
    }

    public QueryParameters(final Type[] positionalParameterTypes, final Object[] positionalParameterValues,
            final Map<String, TypedValue> namedParameters, final Serializable[] collectionKeys) {
        this(positionalParameterTypes, positionalParameterValues, namedParameters, null, null, false, false, false,
                null, null, null, collectionKeys, null);
    }

    public QueryParameters(final Type[] positionalParameterTypes, final Object[] positionalParameterValues,
            final LockOptions lockOptions, final RowSelection rowSelection, final boolean isReadOnlyInitialized,
            final boolean readOnly, final boolean cacheable, final String cacheRegion,
            //final boolean forceCacheRefresh,
            final String comment, final List<String> queryHints, final boolean isLookupByNaturalKey,
            final ResultTransformer transformer) {
        this(positionalParameterTypes, positionalParameterValues, null, lockOptions, rowSelection,
                isReadOnlyInitialized, readOnly, cacheable, cacheRegion, comment, queryHints, null, transformer);
        isNaturalKeyLookup = isLookupByNaturalKey;
    }

    public QueryParameters(final Type[] positionalParameterTypes, final Object[] positionalParameterValues,
            final Map<String, TypedValue> namedParameters, final LockOptions lockOptions,
            final RowSelection rowSelection, final boolean isReadOnlyInitialized, final boolean readOnly,
            final boolean cacheable, final String cacheRegion,
            //final boolean forceCacheRefresh,
            final String comment, final List<String> queryHints, final Serializable[] collectionKeys,
            ResultTransformer transformer) {
        this.positionalParameterTypes = positionalParameterTypes;
        this.positionalParameterValues = positionalParameterValues;
        this.namedParameters = namedParameters;
        this.lockOptions = lockOptions;
        this.rowSelection = rowSelection;
        this.cacheable = cacheable;
        this.cacheRegion = cacheRegion;
        //this.forceCacheRefresh = forceCacheRefresh;
        this.comment = comment;
        this.queryHints = queryHints;
        this.collectionKeys = collectionKeys;
        this.isReadOnlyInitialized = isReadOnlyInitialized;
        this.readOnly = readOnly;
        this.resultTransformer = transformer;
    }

    public QueryParameters(final Type[] positionalParameterTypes, final Object[] positionalParameterValues,
            final Map<String, TypedValue> namedParameters, final LockOptions lockOptions,
            final RowSelection rowSelection, final boolean isReadOnlyInitialized, final boolean readOnly,
            final boolean cacheable, final String cacheRegion,
            //final boolean forceCacheRefresh,
            final String comment, final List<String> queryHints, final Serializable[] collectionKeys,
            final Object optionalObject, final String optionalEntityName, final Serializable optionalId,
            final ResultTransformer transformer) {
        this(positionalParameterTypes, positionalParameterValues, namedParameters, lockOptions, rowSelection,
                isReadOnlyInitialized, readOnly, cacheable, cacheRegion, comment, queryHints, collectionKeys,
                transformer);
        this.optionalEntityName = optionalEntityName;
        this.optionalId = optionalId;
        this.optionalObject = optionalObject;
    }

    public QueryParameters(QueryParameterBindings queryParameterBindings, LockOptions lockOptions,
            RowSelection selection, final boolean isReadOnlyInitialized, boolean readOnly, boolean cacheable,
            String cacheRegion, String comment, List<String> dbHints, final Serializable[] collectionKeys,
            final Object optionalObject, final String optionalEntityName, final Serializable optionalId,
            ResultTransformer resultTransformer) {
        this(queryParameterBindings.collectPositionalBindTypes(),
                queryParameterBindings.collectPositionalBindValues(),
                queryParameterBindings.collectNamedParameterBindings(), lockOptions, selection,
                isReadOnlyInitialized, readOnly, cacheable, cacheRegion, comment, dbHints, collectionKeys,
                optionalObject, optionalEntityName, optionalId, resultTransformer);

    }

    @SuppressWarnings({ "UnusedDeclaration" })
    public boolean hasRowSelection() {
        return rowSelection != null;
    }

    public Map<String, TypedValue> getNamedParameters() {
        return namedParameters;
    }

    public Type[] getPositionalParameterTypes() {
        return positionalParameterTypes;
    }

    public Object[] getPositionalParameterValues() {
        return positionalParameterValues;
    }

    public RowSelection getRowSelection() {
        return rowSelection;
    }

    public ResultTransformer getResultTransformer() {
        return resultTransformer;
    }

    @SuppressWarnings({ "UnusedDeclaration" })
    public void setNamedParameters(Map<String, TypedValue> map) {
        namedParameters = map;
    }

    public void setPositionalParameterTypes(Type[] types) {
        positionalParameterTypes = types;
    }

    public void setPositionalParameterValues(Object[] objects) {
        positionalParameterValues = objects;
    }

    @SuppressWarnings({ "UnusedDeclaration" })
    public void setRowSelection(RowSelection selection) {
        rowSelection = selection;
    }

    public LockOptions getLockOptions() {
        return lockOptions;
    }

    public void setLockOptions(LockOptions lockOptions) {
        this.lockOptions = lockOptions;
    }

    public void traceParameters(SessionFactoryImplementor factory) throws HibernateException {
        EntityPrinter print = new EntityPrinter(factory);
        if (positionalParameterValues.length != 0) {
            LOG.tracev("Parameters: {0}", print.toString(positionalParameterTypes, positionalParameterValues));
        }
        if (namedParameters != null) {
            LOG.tracev("Named parameters: {0}", print.toString(namedParameters));
        }
    }

    public boolean isCacheable() {
        return cacheable;
    }

    public void setCacheable(boolean b) {
        cacheable = b;
    }

    public String getCacheRegion() {
        return cacheRegion;
    }

    public void setCacheRegion(String cacheRegion) {
        this.cacheRegion = cacheRegion;
    }

    public void validateParameters() throws QueryException {
        final int types = positionalParameterTypes == null ? 0 : positionalParameterTypes.length;
        final int values = positionalParameterValues == null ? 0 : positionalParameterValues.length;
        if (types != values) {
            throw new QueryException("Number of positional parameter types:" + types
                    + " does not match number of positional parameters: " + values);
        }
    }

    public String getComment() {
        return comment;
    }

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

    public List<String> getQueryHints() {
        return queryHints;
    }

    public void setQueryHints(List<String> queryHints) {
        this.queryHints = queryHints;
    }

    public ScrollMode getScrollMode() {
        return scrollMode;
    }

    public void setScrollMode(ScrollMode scrollMode) {
        this.scrollMode = scrollMode;
    }

    public Serializable[] getCollectionKeys() {
        return collectionKeys;
    }

    @SuppressWarnings({ "UnusedDeclaration" })
    public void setCollectionKeys(Serializable[] collectionKeys) {
        this.collectionKeys = collectionKeys;
    }

    public String getOptionalEntityName() {
        return optionalEntityName;
    }

    public void setOptionalEntityName(String optionalEntityName) {
        this.optionalEntityName = optionalEntityName;
    }

    public Serializable getOptionalId() {
        return optionalId;
    }

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

    public Object getOptionalObject() {
        return optionalObject;
    }

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

    /**
     * Has the read-only/modifiable mode been explicitly set?
     * @see QueryParameters#setReadOnly(boolean)
     * @see QueryParameters#isReadOnly(SharedSessionContractImplementor)
     *
     * @return true, the read-only/modifiable mode was explicitly set
     *         false, the read-only/modifiable mode was not explicitly set
     */
    public boolean isReadOnlyInitialized() {
        return isReadOnlyInitialized;
    }

    /**
     * Should entities and proxies loaded by the Query be put in read-only mode? The
     * read-only/modifiable setting must be initialized via QueryParameters#setReadOnly(boolean)
     * before calling this method.
     *
     * @see QueryParameters#isReadOnlyInitialized()
     * @see QueryParameters#isReadOnly(SharedSessionContractImplementor)
     * @see QueryParameters#setReadOnly(boolean)
     *
     * The read-only/modifiable setting has no impact on entities/proxies returned by the
     * query that existed in the session before the query was executed.
     *
     * @return true, entities and proxies loaded by the Query will be put in read-only mode
     *         false, entities and proxies loaded by the Query will be put in modifiable mode
     * @throws IllegalStateException if the read-only/modifiable setting has not been
     * initialized (i.e., isReadOnlyInitialized() == false).
     */
    public boolean isReadOnly() {
        if (!isReadOnlyInitialized()) {
            throw new IllegalStateException("cannot call isReadOnly() when isReadOnlyInitialized() returns false");
        }
        return readOnly;
    }

    /**
     * Should entities and proxies loaded by the Query be put in read-only mode?  If the
     * read-only/modifiable setting was not initialized (i.e., QueryParameters#isReadOnlyInitialized() == false),
     * then the default read-only/modifiable setting for the persistence context is returned instead.
     * <p/>
     * The read-only/modifiable setting has no impact on entities/proxies returned by the
     * query that existed in the session before the query was executed.
     *
     * @param session The originating session
     *
     * @return {@code true} indicates that entities and proxies loaded by the query will be put in read-only mode;
     * {@code false} indicates that entities and proxies loaded by the query will be put in modifiable mode
     *
     * @see QueryParameters#isReadOnlyInitialized()
     * @see QueryParameters#setReadOnly(boolean)
     * @see org.hibernate.engine.spi.PersistenceContext#isDefaultReadOnly()
     *
     * The read-only/modifiable setting has no impact on entities/proxies returned by the
     * query that existed in the session before the query was executed.
     *
     */
    public boolean isReadOnly(SharedSessionContractImplementor session) {
        return isReadOnlyInitialized ? isReadOnly() : session.getPersistenceContextInternal().isDefaultReadOnly();
    }

    /**
     * Set the read-only/modifiable mode for entities and proxies loaded by the query.
     * <p/>
     * The read-only/modifiable setting has no impact on entities/proxies returned by the
     * query that existed in the session before the query was executed.
     *
     * @param readOnly if {@code true}, entities and proxies loaded by the query will be put in read-only mode; if
     * {@code false}, entities and proxies loaded by the query will be put in modifiable mode
     *
     * @see QueryParameters#isReadOnlyInitialized()
     * @see QueryParameters#isReadOnly(SharedSessionContractImplementor)
     * @see QueryParameters#setReadOnly(boolean)
     * @see org.hibernate.engine.spi.PersistenceContext#isDefaultReadOnly()
     */
    public void setReadOnly(boolean readOnly) {
        this.readOnly = readOnly;
        this.isReadOnlyInitialized = true;
    }

    public void setCallable(boolean callable) {
        this.callable = callable;
    }

    public boolean isCallable() {
        return callable;
    }

    public boolean hasAutoDiscoverScalarTypes() {
        return autodiscovertypes;
    }

    /**
     * Check if this query should pass the {@code distinct} to the database.
     * @return the query passes {@code distinct} to the database
     */
    public boolean isPassDistinctThrough() {
        return passDistinctThrough;
    }

    /**
     * Set if this query should pass the {@code distinct} to the database.
     * @param passDistinctThrough the query passes {@code distinct} to the database
     */
    public void setPassDistinctThrough(boolean passDistinctThrough) {
        this.passDistinctThrough = passDistinctThrough;
    }

    public void processFilters(String sql, SharedSessionContractImplementor session) {
        processFilters(sql, session.getLoadQueryInfluencers().getEnabledFilters(), session.getFactory());
    }

    @SuppressWarnings({ "unchecked" })
    public void processFilters(String sql, Map filters, SessionFactoryImplementor factory) {
        if (filters.size() == 0 || !sql.contains(ParserHelper.HQL_VARIABLE_PREFIX)) {
            // HELLA IMPORTANT OPTIMIZATION!!!
            processedPositionalParameterValues = getPositionalParameterValues();
            processedPositionalParameterTypes = getPositionalParameterTypes();
            processedSQL = sql;
        } else {
            final StringTokenizer tokens = new StringTokenizer(sql, SYMBOLS, true);
            StringBuilder result = new StringBuilder();
            List parameters = new ArrayList();
            List parameterTypes = new ArrayList();
            int positionalIndex = 0;
            while (tokens.hasMoreTokens()) {
                final String token = tokens.nextToken();
                if (token.startsWith(ParserHelper.HQL_VARIABLE_PREFIX)) {
                    final String filterParameterName = token.substring(1);
                    final String[] parts = LoadQueryInfluencers.parseFilterParameterName(filterParameterName);
                    final FilterImpl filter = (FilterImpl) filters.get(parts[0]);
                    final Object value = filter.getParameter(parts[1]);
                    final Type type = filter.getFilterDefinition().getParameterType(parts[1]);
                    if (value != null && Collection.class.isAssignableFrom(value.getClass())) {
                        Iterator itr = ((Collection) value).iterator();
                        while (itr.hasNext()) {
                            final Object elementValue = itr.next();
                            result.append('?');
                            parameters.add(elementValue);
                            parameterTypes.add(type);
                            if (itr.hasNext()) {
                                result.append(", ");
                            }
                        }
                    } else {
                        result.append('?');
                        parameters.add(value);
                        parameterTypes.add(type);
                    }
                } else {
                    result.append(token);
                    if ("?".equals(token) && positionalIndex < getPositionalParameterValues().length) {
                        final Type type = getPositionalParameterTypes()[positionalIndex];
                        if (type.isComponentType()) {
                            // should process tokens till reaching the number of "?" corresponding to the
                            // numberOfParametersCoveredBy of the compositeType
                            int paramIndex = 1;
                            final int numberOfParametersCoveredBy = getNumberOfParametersCoveredBy(
                                    ((ComponentType) type).getSubtypes());
                            while (paramIndex < numberOfParametersCoveredBy) {
                                final String nextToken = tokens.nextToken();
                                if ("?".equals(nextToken)) {
                                    paramIndex++;
                                }
                                result.append(nextToken);
                            }
                        }
                        parameters.add(getPositionalParameterValues()[positionalIndex]);
                        parameterTypes.add(type);
                        positionalIndex++;
                    }
                }
            }
            processedPositionalParameterValues = parameters.toArray();
            processedPositionalParameterTypes = (Type[]) parameterTypes.toArray(new Type[parameterTypes.size()]);
            processedSQL = result.toString();
        }
    }

    private int getNumberOfParametersCoveredBy(Type[] subtypes) {
        int numberOfParameters = 0;
        for (Type type : subtypes) {
            if (type.isComponentType()) {
                numberOfParameters = numberOfParameters
                        + getNumberOfParametersCoveredBy(((ComponentType) type).getSubtypes());
            } else {
                numberOfParameters++;
            }
        }
        return numberOfParameters;
    }

    public String getFilteredSQL() {
        return processedSQL;
    }

    public Object[] getFilteredPositionalParameterValues() {
        return processedPositionalParameterValues;
    }

    public Type[] getFilteredPositionalParameterTypes() {
        return processedPositionalParameterTypes;
    }

    public boolean isNaturalKeyLookup() {
        return isNaturalKeyLookup;
    }

    @SuppressWarnings({ "UnusedDeclaration" })
    public void setNaturalKeyLookup(boolean isNaturalKeyLookup) {
        this.isNaturalKeyLookup = isNaturalKeyLookup;
    }

    public void setAutoDiscoverScalarTypes(boolean autodiscovertypes) {
        this.autodiscovertypes = autodiscovertypes;
    }

    public QueryParameters createCopyUsing(RowSelection selection) {
        QueryParameters copy = new QueryParameters(this.positionalParameterTypes, this.positionalParameterValues,
                this.namedParameters, this.lockOptions, selection, this.isReadOnlyInitialized, this.readOnly,
                this.cacheable, this.cacheRegion, this.comment, this.queryHints, this.collectionKeys,
                this.optionalObject, this.optionalEntityName, this.optionalId, this.resultTransformer);
        copy.processedSQL = this.processedSQL;
        copy.processedPositionalParameterTypes = this.processedPositionalParameterTypes;
        copy.processedPositionalParameterValues = this.processedPositionalParameterValues;
        copy.passDistinctThrough = this.passDistinctThrough;
        return copy;
    }

    public HQLQueryPlan getQueryPlan() {
        return queryPlan;
    }

    public void setQueryPlan(HQLQueryPlan queryPlan) {
        this.queryPlan = queryPlan;
    }

    public void bindDynamicParameter(Type paramType, Object paramValue) {
        if (processedPositionalParameterTypes != null) {
            int length = processedPositionalParameterTypes.length;
            Type[] types = new Type[length + 1];
            Object[] values = new Object[length + 1];
            for (int i = 0; i < length; i++) {
                types[i] = processedPositionalParameterTypes[i];
                values[i] = processedPositionalParameterValues[i];
            }
            types[length] = paramType;
            values[length] = paramValue;
            processedPositionalParameterTypes = types;
            processedPositionalParameterValues = values;
        }
    }
}