HHH-12116 - Positional parameters report position as name

HHH-12101 - Remove support for legacy HQL-style positional parameters
This commit is contained in:
Steve Ebersole 2017-11-22 11:21:39 -06:00
parent 8308e4252c
commit 5e0274adbb
119 changed files with 2258 additions and 1538 deletions

View File

@ -779,12 +779,12 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase {
doInJPA( this::entityManagerFactory, entityManager -> {
Date timestamp = new Date( );
Session session = entityManager.unwrap( Session.class );
//tag::hql-api-positional-parameter-example[]
//tag::hql-api-positional-parameter-example[]56:37
org.hibernate.query.Query query = session.createQuery(
"select p " +
"from Person p " +
"where p.name like ? " )
.setParameter( 0, "J%" );
"where p.name like ?1" )
.setParameter( 1, "J%" );
//end::hql-api-positional-parameter-example[]
});
}

View File

@ -225,8 +225,8 @@ tokens
return #( [NAMED_PARAM, nameNode.getText()] );
}
protected AST generatePositionalParameter(AST inputNode) throws SemanticException {
return #( [PARAM, "?"] );
protected AST generatePositionalParameter(AST delimiterNode, AST numberNode) throws SemanticException {
return #( [PARAM, numberNode.getText()] );
}
protected void lookupAlias(AST ident) throws SemanticException { }
@ -805,24 +805,13 @@ mapPropertyExpression
parameter!
: #(c:COLON a:identifier) {
// Create a NAMED_PARAM node instead of (COLON IDENT).
#parameter = generateNamedParameter( c, a );
// #parameter = #([NAMED_PARAM,a.getText()]);
// namedParameter(#parameter);
}
| #(p:PARAM (n:NUM_INT)?) {
if ( n != null ) {
// An ejb3-style "positional parameter", which we handle internally as a named-param
#parameter = generateNamedParameter( p, n );
// #parameter = #([NAMED_PARAM,n.getText()]);
// namedParameter(#parameter);
}
else {
#parameter = generatePositionalParameter( p );
// #parameter = #([PARAM,"?"]);
// positionalParameter(#parameter);
}
}
// Create a NAMED_PARAM node instead of (COLON IDENT) - semantics ftw!
#parameter = generateNamedParameter( c, a );
}
| #(p:PARAM (n:NUM_INT)? ) {
// Create a (POSITIONAL_)PARAM node instead of (PARAM NUM_INT) - semantics ftw!
#parameter = generatePositionalParameter( p, n );
}
;
numericInteger

View File

@ -603,6 +603,7 @@ public interface Query<R> extends TypedQuery<R>, CommonQueryContract {
* @return {@code this}, for method chaining
*/
Query<R> setParameterList(String name, Collection values);
Query<R> setParameterList(int position, Collection values);
/**
* Bind multiple values to a named query parameter. This is useful for binding
@ -615,6 +616,7 @@ public interface Query<R> extends TypedQuery<R>, CommonQueryContract {
* @return {@code this}, for method chaining
*/
Query<R> setParameterList(String name, Collection values, Type type);
Query<R> setParameterList(int position, Collection values, Type type);
/**
* Bind multiple values to a named query parameter. This is useful for binding
@ -627,6 +629,7 @@ public interface Query<R> extends TypedQuery<R>, CommonQueryContract {
* @return {@code this}, for method chaining
*/
Query<R> setParameterList(String name, Object[] values, Type type);
Query<R> setParameterList(int position, Object[] values, Type type);
/**
* Bind multiple values to a named query parameter. The Hibernate type of the parameter is
@ -640,6 +643,7 @@ public interface Query<R> extends TypedQuery<R>, CommonQueryContract {
* @return {@code this}, for method chaining
*/
Query<R> setParameterList(String name, Object[] values);
Query<R> setParameterList(int position, Object[] values);
/**
* Bind the property values of the given bean to named parameters of the query,

View File

@ -64,7 +64,61 @@ import org.hibernate.tuple.entity.EntityTuplizerFactory;
import org.jboss.logging.Logger;
import static org.hibernate.cfg.AvailableSettings.*;
import static org.hibernate.cfg.AvailableSettings.ACQUIRE_CONNECTIONS;
import static org.hibernate.cfg.AvailableSettings.ALLOW_JTA_TRANSACTION_ACCESS;
import static org.hibernate.cfg.AvailableSettings.ALLOW_REFRESH_DETACHED_ENTITY;
import static org.hibernate.cfg.AvailableSettings.ALLOW_UPDATE_OUTSIDE_TRANSACTION;
import static org.hibernate.cfg.AvailableSettings.AUTO_CLOSE_SESSION;
import static org.hibernate.cfg.AvailableSettings.AUTO_EVICT_COLLECTION_CACHE;
import static org.hibernate.cfg.AvailableSettings.AUTO_SESSION_EVENTS_LISTENER;
import static org.hibernate.cfg.AvailableSettings.BATCH_FETCH_STYLE;
import static org.hibernate.cfg.AvailableSettings.BATCH_VERSIONED_DATA;
import static org.hibernate.cfg.AvailableSettings.CACHE_REGION_PREFIX;
import static org.hibernate.cfg.AvailableSettings.CHECK_NULLABILITY;
import static org.hibernate.cfg.AvailableSettings.COLLECTION_JOIN_SUBQUERY;
import static org.hibernate.cfg.AvailableSettings.CONNECTION_HANDLING;
import static org.hibernate.cfg.AvailableSettings.CONVENTIONAL_JAVA_CONSTANTS;
import static org.hibernate.cfg.AvailableSettings.CRITERIA_LITERAL_HANDLING_MODE;
import static org.hibernate.cfg.AvailableSettings.CUSTOM_ENTITY_DIRTINESS_STRATEGY;
import static org.hibernate.cfg.AvailableSettings.DEFAULT_BATCH_FETCH_SIZE;
import static org.hibernate.cfg.AvailableSettings.DEFAULT_ENTITY_MODE;
import static org.hibernate.cfg.AvailableSettings.ENABLE_LAZY_LOAD_NO_TRANS;
import static org.hibernate.cfg.AvailableSettings.FLUSH_BEFORE_COMPLETION;
import static org.hibernate.cfg.AvailableSettings.GENERATE_STATISTICS;
import static org.hibernate.cfg.AvailableSettings.HQL_BULK_ID_STRATEGY;
import static org.hibernate.cfg.AvailableSettings.INTERCEPTOR;
import static org.hibernate.cfg.AvailableSettings.JDBC_TIME_ZONE;
import static org.hibernate.cfg.AvailableSettings.JDBC_TYLE_PARAMS_ZERO_BASE;
import static org.hibernate.cfg.AvailableSettings.JPAQL_STRICT_COMPLIANCE;
import static org.hibernate.cfg.AvailableSettings.JTA_TRACK_BY_THREAD;
import static org.hibernate.cfg.AvailableSettings.LOG_SESSION_METRICS;
import static org.hibernate.cfg.AvailableSettings.MAX_FETCH_DEPTH;
import static org.hibernate.cfg.AvailableSettings.MULTI_TENANT_IDENTIFIER_RESOLVER;
import static org.hibernate.cfg.AvailableSettings.ORDER_INSERTS;
import static org.hibernate.cfg.AvailableSettings.ORDER_UPDATES;
import static org.hibernate.cfg.AvailableSettings.PREFER_USER_TRANSACTION;
import static org.hibernate.cfg.AvailableSettings.PROCEDURE_NULL_PARAM_PASSING;
import static org.hibernate.cfg.AvailableSettings.QUERY_CACHE_FACTORY;
import static org.hibernate.cfg.AvailableSettings.QUERY_STARTUP_CHECKING;
import static org.hibernate.cfg.AvailableSettings.QUERY_SUBSTITUTIONS;
import static org.hibernate.cfg.AvailableSettings.RELEASE_CONNECTIONS;
import static org.hibernate.cfg.AvailableSettings.SESSION_FACTORY_NAME;
import static org.hibernate.cfg.AvailableSettings.SESSION_FACTORY_NAME_IS_JNDI;
import static org.hibernate.cfg.AvailableSettings.SESSION_SCOPED_INTERCEPTOR;
import static org.hibernate.cfg.AvailableSettings.STATEMENT_BATCH_SIZE;
import static org.hibernate.cfg.AvailableSettings.STATEMENT_FETCH_SIZE;
import static org.hibernate.cfg.AvailableSettings.STATEMENT_INSPECTOR;
import static org.hibernate.cfg.AvailableSettings.USE_DIRECT_REFERENCE_CACHE_ENTRIES;
import static org.hibernate.cfg.AvailableSettings.USE_GET_GENERATED_KEYS;
import static org.hibernate.cfg.AvailableSettings.USE_IDENTIFIER_ROLLBACK;
import static org.hibernate.cfg.AvailableSettings.USE_MINIMAL_PUTS;
import static org.hibernate.cfg.AvailableSettings.USE_QUERY_CACHE;
import static org.hibernate.cfg.AvailableSettings.USE_SCROLLABLE_RESULTSET;
import static org.hibernate.cfg.AvailableSettings.USE_SECOND_LEVEL_CACHE;
import static org.hibernate.cfg.AvailableSettings.USE_SQL_COMMENTS;
import static org.hibernate.cfg.AvailableSettings.USE_STRUCTURED_CACHE;
import static org.hibernate.cfg.AvailableSettings.VALIDATE_QUERY_PARAMETERS;
import static org.hibernate.cfg.AvailableSettings.WRAP_RESULT_SETS;
import static org.hibernate.engine.config.spi.StandardConverters.BOOLEAN;
import static org.hibernate.jpa.AvailableSettings.DISCARD_PC_ON_CLOSE;
@ -475,6 +529,10 @@ public class SessionFactoryBuilderImpl implements SessionFactoryBuilderImplement
this.options.jtaTransactionAccessEnabled = false;
}
public void enableJdbcStyleParamsZeroBased() {
this.options.jdbcStyleParamsZeroBased = true;
}
@Override
public SessionFactoryOptions buildSessionFactoryOptions() {
return new SessionFactoryOptionsImpl( this );
@ -547,6 +605,7 @@ public class SessionFactoryBuilderImpl implements SessionFactoryBuilderImplement
private boolean conventionalJavaConstants;
private final boolean procedureParameterNullPassingEnabled;
private final boolean collectionJoinSubqueryRewriteEnabled;
private boolean jdbcStyleParamsZeroBased;
// Caching
private boolean secondLevelCacheEnabled;
@ -790,6 +849,12 @@ public class SessionFactoryBuilderImpl implements SessionFactoryBuilderImplement
this.criteriaLiteralHandlingMode = LiteralHandlingMode.interpret(
configurationSettings.get( CRITERIA_LITERAL_HANDLING_MODE )
);
this.jdbcStyleParamsZeroBased = ConfigurationHelper.getBoolean(
JDBC_TYLE_PARAMS_ZERO_BASE,
configurationSettings,
false
);
}
private static Interceptor determineInterceptor(Map configurationSettings, StrategySelector strategySelector) {
@ -1243,6 +1308,11 @@ public class SessionFactoryBuilderImpl implements SessionFactoryBuilderImplement
public LiteralHandlingMode getCriteriaLiteralHandlingMode() {
return this.criteriaLiteralHandlingMode;
}
@Override
public boolean jdbcStyleParamsZeroBased() {
return this.jdbcStyleParamsZeroBased;
}
}
private static Supplier<? extends Interceptor> interceptorSupplier(Class<? extends Interceptor> clazz) {
@ -1592,4 +1662,9 @@ public class SessionFactoryBuilderImpl implements SessionFactoryBuilderImplement
public LiteralHandlingMode getCriteriaLiteralHandlingMode() {
return options.getCriteriaLiteralHandlingMode();
}
@Override
public boolean jdbcStyleParamsZeroBased() {
return options.jdbcStyleParamsZeroBased();
}
}

View File

@ -23,6 +23,7 @@ import org.hibernate.cfg.BaselineSessionEventsListenerBuilder;
import org.hibernate.context.spi.CurrentTenantIdentifierResolver;
import org.hibernate.dialect.function.SQLFunction;
import org.hibernate.hql.spi.id.MultiTableBulkIdStrategy;
import org.hibernate.internal.log.DeprecationLogger;
import org.hibernate.loader.BatchFetchStyle;
import org.hibernate.proxy.EntityNotFoundDelegate;
import org.hibernate.query.criteria.LiteralHandlingMode;
@ -131,6 +132,7 @@ public class SessionFactoryOptionsImpl implements SessionFactoryOptions {
private final Map<String, SQLFunction> sqlFunctions;
private boolean queryParametersValidationEnabled;
private LiteralHandlingMode criteriaLiteralHandlingMode;
private boolean jdbcStyleParamsZeroBased;
public SessionFactoryOptionsImpl(SessionFactoryOptionsState state) {
this.serviceRegistry = state.getServiceRegistry();
@ -186,6 +188,12 @@ public class SessionFactoryOptionsImpl implements SessionFactoryOptions {
this.conventionalJavaConstants = state.isConventionalJavaConstants();
this.procedureParameterNullPassingEnabled = state.isProcedureParameterNullPassingEnabled();
this.collectionJoinSubqueryRewriteEnabled = state.isCollectionJoinSubqueryRewriteEnabled();
this.queryParametersValidationEnabled = state.isQueryParametersValidationEnabled();
this.criteriaLiteralHandlingMode = state.getCriteriaLiteralHandlingMode();
this.jdbcStyleParamsZeroBased = state.jdbcStyleParamsZeroBased();
if ( jdbcStyleParamsZeroBased ) {
DeprecationLogger.DEPRECATION_LOGGER.logUseOfDeprecatedZeroBasedJdbcStyleParams();
}
this.secondLevelCacheEnabled = state.isSecondLevelCacheEnabled();
this.queryCacheEnabled = state.isQueryCacheEnabled();
@ -210,9 +218,6 @@ public class SessionFactoryOptionsImpl implements SessionFactoryOptions {
this.sqlFunctions = state.getCustomSqlFunctionMap();
this.jdbcTimeZone = state.getJdbcTimeZone();
this.queryParametersValidationEnabled = state.isQueryParametersValidationEnabled();
this.criteriaLiteralHandlingMode = state.getCriteriaLiteralHandlingMode();
}
@Override
@ -558,4 +563,9 @@ public class SessionFactoryOptionsImpl implements SessionFactoryOptions {
public LiteralHandlingMode getCriteriaLiteralHandlingMode() {
return criteriaLiteralHandlingMode;
}
@Override
public boolean jdbcStyleParamsZeroBased() {
return jdbcStyleParamsZeroBased;
}
}

View File

@ -203,4 +203,6 @@ public interface SessionFactoryOptionsState {
default LiteralHandlingMode getCriteriaLiteralHandlingMode() {
return LiteralHandlingMode.AUTO;
}
boolean jdbcStyleParamsZeroBased();
}

View File

@ -36,6 +36,11 @@ public abstract class AbstractDelegatingSessionFactoryBuilderImplementor<T exten
delegate().disableJtaTransactionAccess();
}
@Override
public void enableJdbcStyleParamsZeroBased() {
delegate().enableJdbcStyleParamsZeroBased();
}
@Override
public SessionFactoryOptions buildSessionFactoryOptions() {
return delegate().buildSessionFactoryOptions();

View File

@ -395,4 +395,9 @@ public class AbstractDelegatingSessionFactoryOptions implements SessionFactoryOp
public LiteralHandlingMode getCriteriaLiteralHandlingMode() {
return delegate.getCriteriaLiteralHandlingMode();
}
@Override
public boolean jdbcStyleParamsZeroBased() {
return delegate.jdbcStyleParamsZeroBased();
}
}

View File

@ -33,6 +33,11 @@ public interface SessionFactoryBuilderImplementor extends SessionFactoryBuilder
default void disableRefreshDetachedEntity() {
}
/**
* @see org.hibernate.cfg.AvailableSettings#JDBC_TYLE_PARAMS_ZERO_BASE
*/
void enableJdbcStyleParamsZeroBased();
/**
* Build the SessionFactoryOptions that will ultimately be passed to SessionFactoryImpl constructor.
*

View File

@ -248,4 +248,6 @@ public interface SessionFactoryOptions {
default LiteralHandlingMode getCriteriaLiteralHandlingMode() {
return LiteralHandlingMode.AUTO;
}
boolean jdbcStyleParamsZeroBased();
}

View File

@ -10,6 +10,7 @@ import java.util.function.Supplier;
import org.hibernate.boot.MetadataBuilder;
import org.hibernate.boot.registry.classloading.internal.TcclLookupPrecedence;
import org.hibernate.internal.log.DeprecationLogger;
import org.hibernate.query.internal.ParameterMetadataImpl;
import org.hibernate.resource.transaction.spi.TransactionCoordinator;
import org.hibernate.resource.transaction.spi.TransactionCoordinatorBuilder;
@ -477,6 +478,20 @@ public interface AvailableSettings {
*/
String JTA_CACHE_UT = "hibernate.jta.cacheUserTransaction";
/**
* `true` / `false - should zero be used as the base for JDBC-style parameters
* found in native-queries?
*
* @since 5.3
*
* @see DeprecationLogger#logUseOfDeprecatedZeroBasedJdbcStyleParams
*
* @deprecated This is a temporary backwards-compatibility setting to help applications
* using versions prior to 5.3 in upgrading. Deprecation warnings are issued when this
* is set to `true`.
*/
@Deprecated
String JDBC_TYLE_PARAMS_ZERO_BASE = "hibernate.query.sql.jdbc_style_params_base";
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

View File

@ -5,6 +5,7 @@
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.criterion;
import java.io.Serializable;
import org.hibernate.Criteria;

View File

@ -0,0 +1,17 @@
/*
* 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.criterion;
import org.hibernate.type.Type;
/**
* @author Steve Ebersole
*/
public interface ParameterInfoCollector {
void addNamedParameter(String name, Type type);
void addPositionalParameter(int label, Type type);
}

View File

@ -0,0 +1,26 @@
/*
* 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.query;
import org.hibernate.HibernateException;
import org.hibernate.engine.query.spi.ParamLocationRecognizer;
/**
* Indicates a problem during parameter recognition via
* {@link ParamLocationRecognizer}
*
* @author Steve Ebersole
*/
public class ParameterRecognitionException extends HibernateException {
public ParameterRecognitionException(String message) {
super( message );
}
public ParameterRecognitionException(String message, Throwable cause) {
super( message, cause );
}
}

View File

@ -6,13 +6,8 @@
*/
package org.hibernate.engine.query.internal;
import java.util.HashMap;
import java.util.Map;
import org.hibernate.engine.query.spi.NamedParameterDescriptor;
import org.hibernate.engine.query.spi.NativeQueryInterpreter;
import org.hibernate.engine.query.spi.NativeSQLQueryPlan;
import org.hibernate.engine.query.spi.OrdinalParameterDescriptor;
import org.hibernate.engine.query.spi.ParamLocationRecognizer;
import org.hibernate.engine.query.spi.sql.NativeSQLQuerySpecification;
import org.hibernate.engine.spi.SessionFactoryImplementor;
@ -24,46 +19,25 @@ import org.hibernate.query.internal.ParameterMetadataImpl;
* @author Steve Ebersole
*/
public class NativeQueryInterpreterStandardImpl implements NativeQueryInterpreter {
/**
* Singleton access
*/
public static final NativeQueryInterpreterStandardImpl INSTANCE = new NativeQueryInterpreterStandardImpl();
private final SessionFactoryImplementor sessionFactory;
public NativeQueryInterpreterStandardImpl(SessionFactoryImplementor sessionFactory) {
this.sessionFactory = sessionFactory;
}
@Override
public ParameterMetadataImpl getParameterMetadata(String nativeQuery) {
final ParamLocationRecognizer recognizer = ParamLocationRecognizer.parseLocations( nativeQuery );
final int size = recognizer.getOrdinalParameterLocationList().size();
final OrdinalParameterDescriptor[] ordinalDescriptors = new OrdinalParameterDescriptor[ size ];
for ( int i = 0; i < size; i++ ) {
final Integer position = recognizer.getOrdinalParameterLocationList().get( i );
ordinalDescriptors[i] = new OrdinalParameterDescriptor( i, null, position );
}
final Map<String, NamedParameterDescriptor> namedParamDescriptorMap = new HashMap<String, NamedParameterDescriptor>();
final Map<String, ParamLocationRecognizer.NamedParameterDescription> map = recognizer.getNamedParameterDescriptionMap();
for ( final String name : map.keySet() ) {
final ParamLocationRecognizer.NamedParameterDescription description = map.get( name );
namedParamDescriptorMap.put(
name,
new NamedParameterDescriptor(
name,
null,
description.buildPositionsArray(),
description.isJpaStyle()
)
);
}
return new ParameterMetadataImpl( ordinalDescriptors, namedParamDescriptorMap );
final ParamLocationRecognizer recognizer = ParamLocationRecognizer.parseLocations( nativeQuery, sessionFactory );
return new ParameterMetadataImpl(
recognizer.getOrdinalParameterDescriptionMap(),
recognizer.getNamedParameterDescriptionMap()
);
}
@Override
public NativeSQLQueryPlan createQueryPlan(
NativeSQLQuerySpecification specification,
SessionFactoryImplementor sessionFactory) {
CustomQuery customQuery = new SQLCustomQuery(
specification.getQueryString(),
specification.getQueryReturns(),

View File

@ -0,0 +1,57 @@
/*
* 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.query.spi;
import org.hibernate.query.QueryParameter;
import org.hibernate.type.Type;
/**
* @author Steve Ebersole
*/
public abstract class AbstractParameterDescriptor implements QueryParameter {
private final int[] sourceLocations;
private Type expectedType;
public AbstractParameterDescriptor(int[] sourceLocations, Type expectedType) {
this.sourceLocations = sourceLocations;
this.expectedType = expectedType;
}
@Override
public String getName() {
return null;
}
@Override
public Integer getPosition() {
return null;
}
@Override
public Class getParameterType() {
return expectedType == null ? null : expectedType.getReturnedClass();
}
@Override
public Type getType() {
return getExpectedType();
}
@Override
public int[] getSourceLocations() {
return sourceLocations;
}
public Type getExpectedType() {
return expectedType;
}
public void resetExpectedType(Type expectedType) {
this.expectedType = expectedType;
}
}

View File

@ -8,6 +8,7 @@ package org.hibernate.engine.query.spi;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
@ -25,7 +26,9 @@ import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.event.spi.EventSource;
import org.hibernate.hql.internal.QuerySplitter;
import org.hibernate.hql.spi.FilterTranslator;
import org.hibernate.hql.spi.NamedParameterInformation;
import org.hibernate.hql.spi.ParameterTranslations;
import org.hibernate.hql.spi.PositionalParameterInformation;
import org.hibernate.hql.spi.QueryTranslator;
import org.hibernate.hql.spi.QueryTranslatorFactory;
import org.hibernate.internal.CoreLogging;
@ -58,6 +61,7 @@ public class HQLQueryPlan implements Serializable {
private final Set<String> enabledFilterNames;
private final boolean shallow;
private final SessionFactoryImplementor factory;
/**
* We'll check the trace level only once per instance
@ -92,8 +96,9 @@ public class HQLQueryPlan implements Serializable {
EntityGraphQueryHint entityGraphQueryHint) {
this.sourceQuery = hql;
this.shallow = shallow;
this.factory = factory;
final Set<String> copy = new HashSet<String>();
final Set<String> copy = new HashSet<>();
copy.addAll( enabledFilters.keySet() );
this.enabledFilterNames = java.util.Collections.unmodifiableSet( copy );
@ -101,8 +106,8 @@ public class HQLQueryPlan implements Serializable {
final int length = concreteQueryStrings.length;
this.translators = new QueryTranslator[length];
final List<String> sqlStringList = new ArrayList<String>();
final Set<Serializable> combinedQuerySpaces = new HashSet<Serializable>();
final List<String> sqlStringList = new ArrayList<>();
final Set<Serializable> combinedQuerySpaces = new HashSet<>();
final Map querySubstitutions = factory.getSessionFactoryOptions().getQuerySubstitutions();
final QueryTranslatorFactory queryTranslatorFactory = factory.getServiceRegistry().getService( QueryTranslatorFactory.class );
@ -151,7 +156,7 @@ public class HQLQueryPlan implements Serializable {
}
public ParameterMetadataImpl getParameterMetadata() {
return parameterMetadata.getOrdinalParametersZeroBasedCopy();
return parameterMetadata;
}
public ReturnMetadata getReturnMetadata() {
@ -377,46 +382,53 @@ public class HQLQueryPlan implements Serializable {
}
private ParameterMetadataImpl buildParameterMetadata(ParameterTranslations parameterTranslations, String hql) {
final long start = traceEnabled ? System.nanoTime() : 0;
final ParamLocationRecognizer recognizer = ParamLocationRecognizer.parseLocations( hql );
if ( traceEnabled ) {
final long end = System.nanoTime();
LOG.tracev( "HQL param location recognition took {0} nanoseconds ({1})", ( end - start ), hql );
final Map<Integer,OrdinalParameterDescriptor> ordinalParamDescriptors;
if ( parameterTranslations.getPositionalParameterInformationMap().isEmpty() ) {
ordinalParamDescriptors = Collections.emptyMap();
}
else {
final Map<Integer,OrdinalParameterDescriptor> temp = new HashMap<>();
for ( Map.Entry<Integer, PositionalParameterInformation> entry :
parameterTranslations.getPositionalParameterInformationMap().entrySet() ) {
final int position = entry.getKey();
temp.put(
position,
new OrdinalParameterDescriptor(
position,
position - 1,
entry.getValue().getExpectedType(),
entry.getValue().getSourceLocations()
)
);
}
ordinalParamDescriptors = Collections.unmodifiableMap( temp );
}
int ordinalParamCount = parameterTranslations.getOrdinalParameterCount();
final int[] locations = ArrayHelper.toIntArray( recognizer.getOrdinalParameterLocationList() );
if ( parameterTranslations.supportsOrdinalParameterMetadata() && locations.length != ordinalParamCount ) {
throw new HibernateException( "ordinal parameter mismatch" );
}
ordinalParamCount = locations.length;
final OrdinalParameterDescriptor[] ordinalParamDescriptors = new OrdinalParameterDescriptor[ordinalParamCount];
for ( int i = 0; i < ordinalParamCount; i++ ) {
ordinalParamDescriptors[ i ] = new OrdinalParameterDescriptor(
i,
parameterTranslations.supportsOrdinalParameterMetadata()
? parameterTranslations.getOrdinalParameterExpectedType( i )
: null,
locations[ i ]
);
final Map<String, NamedParameterDescriptor> namedParamDescriptorMap;
if ( parameterTranslations.getNamedParameterInformationMap().isEmpty() ) {
namedParamDescriptorMap = Collections.emptyMap();
}
else {
final Map<String, NamedParameterDescriptor> tmp = new HashMap<>();
for ( Map.Entry<String, NamedParameterInformation> namedEntry :
parameterTranslations.getNamedParameterInformationMap().entrySet() ) {
final String name = namedEntry.getKey();
tmp.put(
name,
new NamedParameterDescriptor(
name,
parameterTranslations.getNamedParameterInformation( name ).getExpectedType(),
namedEntry.getValue().getSourceLocations()
)
);
}
namedParamDescriptorMap = Collections.unmodifiableMap( tmp );
}
final Map<String, NamedParameterDescriptor> namedParamDescriptorMap = new HashMap<String, NamedParameterDescriptor>();
final Map<String, ParamLocationRecognizer.NamedParameterDescription> map = recognizer.getNamedParameterDescriptionMap();
for ( final String name : map.keySet() ) {
final ParamLocationRecognizer.NamedParameterDescription description = map.get( name );
namedParamDescriptorMap.put(
name,
new NamedParameterDescriptor(
name,
parameterTranslations.getNamedParameterExpectedType( name ),
description.buildPositionsArray(),
description.isJpaStyle()
)
);
}
return new ParameterMetadataImpl( ordinalParamDescriptors, namedParamDescriptorMap );
}

View File

@ -7,7 +7,6 @@
package org.hibernate.engine.query.spi;
import org.hibernate.Incubating;
import org.hibernate.query.QueryParameter;
import org.hibernate.type.Type;
/**
@ -16,11 +15,8 @@ import org.hibernate.type.Type;
* @author Steve Ebersole
*/
@Incubating
public class NamedParameterDescriptor implements QueryParameter {
public class NamedParameterDescriptor extends AbstractParameterDescriptor {
private final String name;
private Type expectedType;
private final int[] sourceLocations;
private final boolean jpaStyle;
/**
* Constructs a NamedParameterDescriptor
@ -28,60 +24,16 @@ public class NamedParameterDescriptor implements QueryParameter {
* @param name The name of the parameter
* @param expectedType The expected type of the parameter, according to the translator
* @param sourceLocations The locations of the named parameters (aye aye aye)
* @param jpaStyle Was the parameter a JPA style "named parameter"?
*/
public NamedParameterDescriptor(String name, Type expectedType, int[] sourceLocations, boolean jpaStyle) {
public NamedParameterDescriptor(String name, Type expectedType, int[] sourceLocations) {
super( sourceLocations, expectedType );
this.name = name;
this.expectedType = expectedType;
this.sourceLocations = sourceLocations;
this.jpaStyle = jpaStyle;
}
public String getName() {
return name;
}
@Override
public Integer getPosition() {
return null;
}
@Override
public Class getParameterType() {
return expectedType == null ? null : expectedType.getReturnedClass();
}
@Override
public boolean isJpaPositionalParameter() {
return isJpaStyle();
}
public Type getExpectedType() {
return expectedType;
}
public int[] getSourceLocations() {
return sourceLocations;
}
public boolean isJpaStyle() {
return jpaStyle;
}
/**
* Set the parameters expected type
*
* @param type The new expected type
*/
public void resetExpectedType(Type type) {
this.expectedType = type;
}
@Override
public Type getType() {
return expectedType;
}
@Override
public boolean equals(Object o) {
if ( this == o ) {

View File

@ -26,7 +26,7 @@ public class NativeQueryInterpreterInitiator implements SessionFactoryServiceIni
SessionFactoryImplementor sessionFactory,
SessionFactoryOptions sessionFactoryOptions,
ServiceRegistryImplementor registry) {
return NativeQueryInterpreterStandardImpl.INSTANCE;
return new NativeQueryInterpreterStandardImpl( sessionFactory );
}
@Override

View File

@ -9,22 +9,16 @@ package org.hibernate.engine.query.spi;
import java.io.Serializable;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.hibernate.HibernateException;
import org.hibernate.QueryException;
import org.hibernate.action.internal.BulkOperationCleanupAction;
import org.hibernate.engine.spi.QueryParameters;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.engine.spi.TypedValue;
import org.hibernate.event.spi.EventSource;
import org.hibernate.internal.CoreLogging;
import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.internal.util.collections.ArrayHelper;
import org.hibernate.loader.custom.CustomQuery;
import org.hibernate.type.Type;
import org.hibernate.param.ParameterBinder;
/**
* Defines a query execution plan for a native-SQL query.
@ -56,98 +50,6 @@ public class NativeSQLQueryPlan implements Serializable {
return customQuery;
}
private int[] getNamedParameterLocs(String name) throws QueryException {
final Object loc = customQuery.getNamedParameterBindPoints().get( name );
if ( loc == null ) {
throw new QueryException(
"Named parameter does not appear in Query: " + name,
customQuery.getSQL() );
}
if ( loc instanceof Integer ) {
return new int[] { (Integer) loc };
}
else {
return ArrayHelper.toIntArray( (List) loc );
}
}
/**
* Perform binding of all the JDBC bind parameter values based on the user-defined
* positional query parameters (these are the '?'-style hibernate query
* params) into the JDBC {@link PreparedStatement}.
*
* @param st The prepared statement to which to bind the parameter values.
* @param queryParameters The query parameters specified by the application.
* @param start JDBC paramer binds are positional, so this is the position
* from which to start binding.
* @param session The session from which the query originated.
*
* @return The number of JDBC bind positions accounted for during execution.
*
* @throws SQLException Some form of JDBC error binding the values.
* @throws HibernateException Generally indicates a mapping problem or type mismatch.
*/
private int bindPositionalParameters(
final PreparedStatement st,
final QueryParameters queryParameters,
final int start,
final SharedSessionContractImplementor session) throws SQLException {
final Object[] values = queryParameters.getFilteredPositionalParameterValues();
final Type[] types = queryParameters.getFilteredPositionalParameterTypes();
int span = 0;
for (int i = 0; i < values.length; i++) {
types[i].nullSafeSet( st, values[i], start + span, session );
span += types[i].getColumnSpan( session.getFactory() );
}
return span;
}
/**
* Perform binding of all the JDBC bind parameter values based on the user-defined
* named query parameters into the JDBC {@link PreparedStatement}.
*
* @param ps The prepared statement to which to bind the parameter values.
* @param namedParams The named query parameters specified by the application.
* @param start JDBC paramer binds are positional, so this is the position
* from which to start binding.
* @param session The session from which the query originated.
*
* @return The number of JDBC bind positions accounted for during execution.
*
* @throws SQLException Some form of JDBC error binding the values.
* @throws HibernateException Generally indicates a mapping problem or type mismatch.
*/
private int bindNamedParameters(
final PreparedStatement ps,
final Map namedParams,
final int start,
final SharedSessionContractImplementor session) throws SQLException {
if ( namedParams != null ) {
// assumes that types are all of span 1
final Iterator iter = namedParams.entrySet().iterator();
int result = 0;
while ( iter.hasNext() ) {
final Map.Entry e = (Map.Entry) iter.next();
final String name = (String) e.getKey();
final TypedValue typedval = (TypedValue) e.getValue();
final int[] locs = getNamedParameterLocs( name );
for ( int loc : locs ) {
LOG.debugf( "bindNamedParameters() %s -> %s [%s]", typedval.getValue(), name, loc + start );
typedval.getType().nullSafeSet(
ps,
typedval.getValue(),
loc + start,
session
);
}
result += locs.length;
}
return result;
}
return 0;
}
protected void coordinateSharedCacheCleanup(SharedSessionContractImplementor session) {
final BulkOperationCleanupAction action = new BulkOperationCleanupAction( session, getCustomQuery().getQuerySpaces() );
@ -194,8 +96,9 @@ public class NativeSQLQueryPlan implements Serializable {
try {
int col = 1;
col += bindPositionalParameters( ps, queryParameters, col, session );
col += bindNamedParameters( ps, queryParameters.getNamedParameters(), col, session );
for ( ParameterBinder binder : this.customQuery.getParameterValueBinders() ) {
col += binder.bind( ps, queryParameters, session, col );
}
result = session.getJdbcCoordinator().getResultSetReturn().executeUpdate( ps );
}
finally {

View File

@ -7,7 +7,6 @@
package org.hibernate.engine.query.spi;
import org.hibernate.Incubating;
import org.hibernate.query.QueryParameter;
import org.hibernate.type.Type;
/**
@ -16,58 +15,29 @@ import org.hibernate.type.Type;
* @author Steve Ebersole
*/
@Incubating
public class OrdinalParameterDescriptor implements QueryParameter {
private final int ordinalPosition;
private final Type expectedType;
private final int sourceLocation;
public class OrdinalParameterDescriptor extends AbstractParameterDescriptor {
private final int label;
private final int valuePosition;
/**
* Constructs an ordinal parameter descriptor.
*
* @param ordinalPosition The ordinal position
* @param expectedType The expected type of the parameter
* @param sourceLocation The location of the parameter
*/
public OrdinalParameterDescriptor(int ordinalPosition, Type expectedType, int sourceLocation) {
this.ordinalPosition = ordinalPosition;
this.expectedType = expectedType;
this.sourceLocation = sourceLocation;
}
public int getOrdinalPosition() {
return ordinalPosition;
}
public Type getExpectedType() {
return expectedType;
}
public int getSourceLocation() {
return sourceLocation;
}
@Override
public Type getType() {
return expectedType;
}
@Override
public String getName() {
return null;
public OrdinalParameterDescriptor(
int label,
int valuePosition,
Type expectedType,
int[] sourceLocations) {
super( sourceLocations, expectedType );
this.label = label;
this.valuePosition = valuePosition;
}
@Override
public Integer getPosition() {
return ordinalPosition;
return label;
}
@Override
public Class getParameterType() {
return expectedType == null ? null : expectedType.getReturnedClass();
}
@Override
public boolean isJpaPositionalParameter() {
return false;
public int getValuePosition() {
return valuePosition;
}
}

View File

@ -7,10 +7,13 @@
package org.hibernate.engine.query.spi;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.hibernate.engine.query.ParameterRecognitionException;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.internal.util.collections.ArrayHelper;
/**
@ -20,97 +23,234 @@ import org.hibernate.internal.util.collections.ArrayHelper;
* @author Steve Ebersole
*/
public class ParamLocationRecognizer implements ParameterParser.Recognizer {
/**
* Internal representation of a recognized named parameter
*/
public static class NamedParameterDescription {
private final boolean jpaStyle;
private final List<Integer> positions = new ArrayList<Integer>();
NamedParameterDescription(boolean jpaStyle) {
this.jpaStyle = jpaStyle;
}
private Map<String, NamedParameterDescriptor> namedParameterDescriptors;
private Map<Integer, OrdinalParameterDescriptor> ordinalParameterDescriptors;
public boolean isJpaStyle() {
return jpaStyle;
}
private Map<String, InFlightNamedParameterState> inFlightNamedStateMap;
private Map<Integer, InFlightOrdinalParameterState> inFlightOrdinalStateMap;
private Map<Integer, InFlightJpaOrdinalParameterState> inFlightJpaOrdinalStateMap;
private void add(int position) {
positions.add( position );
}
private final int jdbcStyleOrdinalCountBase;
private int jdbcStyleOrdinalCount;
public int[] buildPositionsArray() {
return ArrayHelper.toIntArray( positions );
}
public ParamLocationRecognizer(int jdbcStyleOrdinalCountBase) {
this.jdbcStyleOrdinalCountBase = jdbcStyleOrdinalCountBase;
this.jdbcStyleOrdinalCount = jdbcStyleOrdinalCountBase;
}
private Map<String, NamedParameterDescription> namedParameterDescriptions = new HashMap<String, NamedParameterDescription>();
private List<Integer> ordinalParameterLocationList = new ArrayList<Integer>();
/**
* Convenience method for creating a param location recognizer and
* initiating the parse.
*
* @param query The query to be parsed for parameter locations.
* @param sessionFactory
* @return The generated recognizer, with journaled location info.
*/
public static ParamLocationRecognizer parseLocations(String query) {
final ParamLocationRecognizer recognizer = new ParamLocationRecognizer();
public static ParamLocationRecognizer parseLocations(
String query,
SessionFactoryImplementor sessionFactory) {
final ParamLocationRecognizer recognizer = new ParamLocationRecognizer(
sessionFactory.getSessionFactoryOptions().jdbcStyleParamsZeroBased() ? 0 : 1
);
ParameterParser.parse( query, recognizer );
return recognizer;
}
/**
* Returns the map of named parameter locations. The map is keyed by
* parameter name; the corresponding value is a (@link NamedParameterDescription}.
*
* @return The map of named parameter locations.
*/
public Map<String, NamedParameterDescription> getNamedParameterDescriptionMap() {
return namedParameterDescriptions;
@Override
public void complete() {
if ( inFlightNamedStateMap != null && ( inFlightOrdinalStateMap != null || inFlightJpaOrdinalStateMap != null ) ) {
throw mixedParamStrategy();
}
// we know `inFlightNamedStateMap` is null, so no need to check it again
if ( inFlightOrdinalStateMap != null && inFlightJpaOrdinalStateMap != null ) {
throw mixedParamStrategy();
}
if ( inFlightNamedStateMap != null ) {
final Map<String, NamedParameterDescriptor> tmp = new HashMap<>();
for ( InFlightNamedParameterState inFlightState : inFlightNamedStateMap.values() ) {
tmp.put( inFlightState.name, inFlightState.complete() );
}
namedParameterDescriptors = Collections.unmodifiableMap( tmp );
}
else {
namedParameterDescriptors = Collections.emptyMap();
}
if ( inFlightOrdinalStateMap == null && inFlightJpaOrdinalStateMap == null ) {
ordinalParameterDescriptors = Collections.emptyMap();
}
else {
final Map<Integer, OrdinalParameterDescriptor> tmp = new HashMap<>();
if ( inFlightOrdinalStateMap != null ) {
for ( InFlightOrdinalParameterState state : inFlightOrdinalStateMap.values() ) {
tmp.put( state.identifier, state.complete() );
}
}
else {
for ( InFlightJpaOrdinalParameterState state : inFlightJpaOrdinalStateMap.values() ) {
tmp.put( state.identifier, state.complete() );
}
}
ordinalParameterDescriptors = Collections.unmodifiableMap( tmp );
}
}
/**
* Returns the list of ordinal parameter locations. The list elements
* are Integers, representing the location for that given ordinal. Thus calling
* {@code getOrdinalParameterLocationList().elementAt(n)} represents the
* location for the nth parameter.
*
* @return The list of ordinal parameter locations.
*/
public List<Integer> getOrdinalParameterLocationList() {
return ordinalParameterLocationList;
private ParameterRecognitionException mixedParamStrategy() {
throw new ParameterRecognitionException( "Mixed parameter strategies - use just one of named, positional or JPA-ordinal strategy" );
}
public Map<String, NamedParameterDescriptor> getNamedParameterDescriptionMap() {
return namedParameterDescriptors;
}
public Map<Integer, OrdinalParameterDescriptor> getOrdinalParameterDescriptionMap() {
return ordinalParameterDescriptors;
}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Recognition code ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// NOTE : we keep track of `inFlightOrdinalStateMap` versus `inFlightJpaOrdinalStateMap`
// in order to perform better validations of mixed parameter strategies
@Override
public void ordinalParameter(int position) {
ordinalParameterLocationList.add( position );
}
@Override
public void namedParameter(String name, int position) {
getOrBuildNamedParameterDescription( name, false ).add( position );
}
@Override
public void jpaPositionalParameter(String name, int position) {
getOrBuildNamedParameterDescription( name, true ).add( position );
if ( inFlightOrdinalStateMap == null ) {
inFlightOrdinalStateMap = new HashMap<>();
}
final int label = jdbcStyleOrdinalCount++;
inFlightOrdinalStateMap.put(
label,
new InFlightOrdinalParameterState( label, label - jdbcStyleOrdinalCountBase, position )
);
}
private NamedParameterDescription getOrBuildNamedParameterDescription(String name, boolean jpa) {
NamedParameterDescription desc = namedParameterDescriptions.get( name );
if ( desc == null ) {
desc = new NamedParameterDescription( jpa );
namedParameterDescriptions.put( name, desc );
}
return desc;
@Override
public void namedParameter(String name, int position) {
getOrBuildNamedParameterDescription( name ).add( position );
}
private InFlightNamedParameterState getOrBuildNamedParameterDescription(String name) {
if ( inFlightNamedStateMap == null ) {
inFlightNamedStateMap = new HashMap<>();
}
InFlightNamedParameterState descriptor = inFlightNamedStateMap.get( name );
if ( descriptor == null ) {
descriptor = new InFlightNamedParameterState( name );
inFlightNamedStateMap.put( name, descriptor );
}
return descriptor;
}
@Override
public void jpaPositionalParameter(int name, int position) {
getOrBuildJpaOrdinalParameterDescription( name ).add( position );
}
private InFlightJpaOrdinalParameterState getOrBuildJpaOrdinalParameterDescription(int name) {
if ( inFlightJpaOrdinalStateMap == null ) {
inFlightJpaOrdinalStateMap = new HashMap<>();
}
InFlightJpaOrdinalParameterState descriptor = inFlightJpaOrdinalStateMap.get( name );
if ( descriptor == null ) {
descriptor = new InFlightJpaOrdinalParameterState( name );
inFlightJpaOrdinalStateMap.put( name, descriptor );
}
return descriptor;
}
@Override
public void other(char character) {
// don't care...
}
@Override
public void outParameter(int position) {
// don't care...
}
/**
* Internal in-flight representation of a recognized named parameter
*/
public static class InFlightNamedParameterState {
private final String name;
private final List<Integer> sourcePositions = new ArrayList<>();
InFlightNamedParameterState(String name) {
this.name = name;
}
private void add(int position) {
sourcePositions.add( position );
}
private NamedParameterDescriptor complete() {
return new NamedParameterDescriptor(
name,
null,
ArrayHelper.toIntArray( sourcePositions )
);
}
}
/**
* Internal in-flight representation of a recognized named parameter
*/
public static class InFlightOrdinalParameterState {
private final int identifier;
private final int valuePosition;
private final int sourcePosition;
InFlightOrdinalParameterState(int label, int valuePosition, int sourcePosition) {
this.identifier = label;
this.valuePosition = valuePosition;
this.sourcePosition = sourcePosition;
}
private OrdinalParameterDescriptor complete() {
return new OrdinalParameterDescriptor(
identifier,
valuePosition,
null,
new int[] { sourcePosition }
);
}
}
/**
* Internal in-flight representation of a recognized named parameter
*/
public static class InFlightJpaOrdinalParameterState {
private final int identifier;
private final List<Integer> sourcePositions = new ArrayList<>();
InFlightJpaOrdinalParameterState(int identifier) {
this.identifier = identifier;
}
private void add(int position) {
sourcePositions.add( position );
}
private OrdinalParameterDescriptor complete() {
return new OrdinalParameterDescriptor(
identifier,
identifier - 1,
null,
ArrayHelper.toIntArray( sourcePositions )
);
}
}
}

View File

@ -22,20 +22,20 @@ public class ParameterParser {
/**
* Maybe better named a Journaler. Essentially provides a callback contract for things that recognize parameters
*/
public static interface Recognizer {
public interface Recognizer {
/**
* Called when an output parameter is recognized
*
* @param position The position within the query
*/
public void outParameter(int position);
void outParameter(int position);
/**
* Called when an ordinal parameter is recognized
*
* @param position The position within the query
*/
public void ordinalParameter(int position);
void ordinalParameter(int position);
/**
* Called when a named parameter is recognized
@ -43,22 +43,24 @@ public class ParameterParser {
* @param name The recognized parameter name
* @param position The position within the query
*/
public void namedParameter(String name, int position);
void namedParameter(String name, int position);
/**
* Called when a JPA-style named parameter is recognized
*
* @param name The name of the JPA-style parameter
* @param identifier The identifier (name) of the JPA-style parameter
* @param position The position within the query
*/
public void jpaPositionalParameter(String name, int position);
void jpaPositionalParameter(int identifier, int position);
/**
* Called when a character that is not a parameter (or part of a parameter dfinition) is recognized.
*
* @param character The recognized character
*/
public void other(char character);
void other(char character);
void complete();
}
/**
@ -179,20 +181,19 @@ public class ParameterParser {
}
else if ( c == '?' ) {
// could be either an ordinal or JPA-positional parameter
if ( indx < stringLength - 1 && Character.isDigit( sqlString.charAt( indx + 1 ) ) ) {
if ( indx < stringLength - 2 && Character.isDigit( sqlString.charAt( indx + 1 ) ) ) {
// a peek ahead showed this as an JPA-positional parameter
final int right = StringHelper.firstIndexOfChar( sqlString, ParserHelper.HQL_SEPARATORS, indx + 1 );
final int chopLocation = right < 0 ? sqlString.length() : right;
final String param = sqlString.substring( indx + 1, chopLocation );
// make sure this "name" is an integral
try {
Integer.valueOf( param );
recognizer.jpaPositionalParameter( Integer.valueOf( param ), indx );
indx = chopLocation - 1;
}
catch( NumberFormatException e ) {
throw new QueryException( "JPA-style positional param was not an integral ordinal" );
}
recognizer.jpaPositionalParameter( param, indx );
indx = chopLocation - 1;
}
else {
if ( hasMainOutputParameter && !foundMainOutputParam ) {
@ -209,6 +210,8 @@ public class ParameterParser {
}
}
}
recognizer.complete();
}
/**

View File

@ -45,6 +45,7 @@ public final class QueryParameters {
private Type[] positionalParameterTypes;
private Object[] positionalParameterValues;
private Map<String,TypedValue> namedParameters;
private LockOptions lockOptions;
private RowSelection rowSelection;
private boolean cacheable;

View File

@ -20,23 +20,24 @@ import antlr.RecognitionException;
/**
* An error handler that counts parsing errors and warnings.
*/
public class ErrorCounter implements ParseErrorHandler {
public class ErrorTracker implements ParseErrorHandler {
private static final CoreMessageLogger LOG = Logger.getMessageLogger(
CoreMessageLogger.class,
ErrorCounter.class.getName()
ErrorTracker.class.getName()
);
private final String hql;
private List<String> errorList = new ArrayList<String>();
private List<RecognitionException> recognitionExceptions = new ArrayList<RecognitionException>();
private List<String> errorList = new ArrayList<>();
private List<RecognitionException> recognitionExceptions = new ArrayList<>();
/**
* Constructs an ErrorCounter without knowledge of the HQL, meaning that generated QueryException
* instances *will not* contain the HQL (and will need to be wrapped at a higher level in another
* QueryException).
*/
public ErrorCounter() {
@SuppressWarnings("WeakerAccess")
public ErrorTracker() {
this( null );
}
@ -44,7 +45,8 @@ public class ErrorCounter implements ParseErrorHandler {
* Constructs an ErrorCounter with knowledge of the HQL, meaning that generated QueryException
* instances *will* contain the HQL.
*/
public ErrorCounter(String hql) {
@SuppressWarnings("WeakerAccess")
public ErrorTracker(String hql) {
this.hql = hql;
}

View File

@ -61,7 +61,7 @@ public final class HqlParser extends HqlBaseParser {
private HqlParser(String hql) {
// The fix for HHH-558...
super( new HqlLexer( new StringReader( hql ) ) );
parseErrorHandler = new ErrorCounter( hql );
parseErrorHandler = new ErrorTracker( hql );
// Create nodes that track line and column number.
setASTFactory( new HqlASTFactory() );
}

View File

@ -15,6 +15,7 @@ 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;
@ -94,6 +95,8 @@ import antlr.RecognitionException;
import antlr.SemanticException;
import antlr.collections.AST;
import static org.hibernate.hql.spi.QueryTranslator.ERROR_LEGACY_ORDINAL_PARAMS_NO_LONGER_SUPPORTED;
/**
* Implements methods used by the HQL->SQL tree transform grammar (a.k.a. the second phase).
* <ul>
@ -125,15 +128,16 @@ public class HqlSqlWalker extends HqlSqlBaseWalker implements ErrorReporter, Par
* Maps each top-level result variable to its SelectExpression;
* (excludes result variables defined in subqueries)
*/
private Map<String, SelectExpression> selectExpressionsByResultVariable = new HashMap<String, SelectExpression>();
private Map<String, SelectExpression> selectExpressionsByResultVariable = new HashMap<>();
private Set<Serializable> querySpaces = new HashSet<Serializable>();
private Set<Serializable> querySpaces = new HashSet<>();
private int parameterCount;
private Map namedParameters = new HashMap();
private ArrayList<ParameterSpecification> parameters = new ArrayList<ParameterSpecification>();
private Map namedParameters;
private Map positionalParameters;
private ArrayList<ParameterSpecification> parameterSpecs = new ArrayList<>();
private int numberOfParametersInSetClause;
private int positionalParameterCount;
private ArrayList assignmentSpecifications = new ArrayList();
@ -160,7 +164,7 @@ public class HqlSqlWalker extends HqlSqlBaseWalker implements ErrorReporter, Par
String collectionRole) {
setASTFactory( new SqlASTFactory( this ) );
// Initialize the error handling delegate.
this.parseErrorHandler = new ErrorCounter( qti.getQueryString() );
this.parseErrorHandler = new ErrorTracker( qti.getQueryString() );
this.queryTranslatorImpl = qti;
this.sessionFactoryHelper = new SessionFactoryHelper( sfi );
this.literalProcessor = new LiteralProcessor( this );
@ -227,7 +231,7 @@ public class HqlSqlWalker extends HqlSqlBaseWalker implements ErrorReporter, Par
// positionalParameterCount++
// );
// collectionFilterKeyParameter.setHqlParameterSpecification( paramSpec );
// parameters.add( paramSpec );
// parameterSpecs.add( paramSpec );
// }
// }
// }
@ -252,14 +256,16 @@ public class HqlSqlWalker extends HqlSqlBaseWalker implements ErrorReporter, Par
queryTranslatorImpl.showHqlAst( hqlParser.getAST() );
// Create a parameter specification for the collection filter...
Type collectionFilterKeyType = sessionFactoryHelper.requireQueryableCollection( collectionFilterRole )
final Type collectionFilterKeyType = sessionFactoryHelper.requireQueryableCollection( collectionFilterRole )
.getKeyType();
ParameterNode collectionFilterKeyParameter = (ParameterNode) astFactory.create( PARAM, "?" );
CollectionFilterKeyParameterSpecification collectionFilterKeyParameterSpec = new CollectionFilterKeyParameterSpecification(
collectionFilterRole, collectionFilterKeyType, positionalParameterCount++
final ParameterNode collectionFilterKeyParameter = (ParameterNode) astFactory.create( PARAM, "?" );
final CollectionFilterKeyParameterSpecification collectionFilterKeyParameterSpec = new CollectionFilterKeyParameterSpecification(
collectionFilterRole,
collectionFilterKeyType
);
parameterCount++;
collectionFilterKeyParameter.setHqlParameterSpecification( collectionFilterKeyParameterSpec );
parameters.add( collectionFilterKeyParameterSpec );
parameterSpecs.add( collectionFilterKeyParameterSpec );
}
}
}
@ -936,7 +942,7 @@ public class HqlSqlWalker extends HqlSqlBaseWalker implements ErrorReporter, Par
versionValueNode = getASTFactory().create( HqlSqlTokenTypes.PARAM, "?" );
ParameterSpecification paramSpec = new VersionTypeSeedParameterSpecification( versionType );
( (ParameterNode) versionValueNode ).setHqlParameterSpecification( paramSpec );
parameters.add( 0, paramSpec );
parameterSpecs.add( 0, paramSpec );
if ( sessionFactoryHelper.getFactory().getDialect().requiresCastingOfParametersInSelectClause() ) {
// we need to wrtap the param in a cast()
@ -977,7 +983,7 @@ public class HqlSqlWalker extends HqlSqlBaseWalker implements ErrorReporter, Par
versionValueNode = getASTFactory().create( HqlSqlTokenTypes.SQL_TOKEN, functionName );
}
else {
throw new QueryException( "cannot handle version type [" + versionType + "] on bulk inserts with dialects not supporting parameters in insert-select statements" );
throw new QueryException( "cannot handle version type [" + versionType + "] on bulk inserts with dialects not supporting parameterSpecs in insert-select statements" );
}
}
@ -1081,64 +1087,109 @@ public class HqlSqlWalker extends HqlSqlBaseWalker implements ErrorReporter, Par
}
@Override
protected AST generatePositionalParameter(AST inputNode) throws SemanticException {
if ( namedParameters.size() > 0 ) {
protected AST generatePositionalParameter(AST delimiterNode, AST numberNode) throws SemanticException {
// todo : we check this multiple times
if ( namedParameters != null ) {
throw new SemanticException(
"cannot define positional parameter after any named parameters have been defined"
"Cannot define positional and named parameterSpecs : " + queryTranslatorImpl.getQueryString()
);
}
LOG.warnf(
"[DEPRECATION] Encountered positional parameter near line %s, column %s in HQL: [%s]. Positional parameter " +
"are considered deprecated; use named parameters or JPA-style positional parameters instead.",
inputNode.getLine(),
inputNode.getColumn(),
queryTranslatorImpl.getQueryString()
);
ParameterNode parameter = (ParameterNode) astFactory.create( PARAM, "?" );
PositionalParameterSpecification paramSpec = new PositionalParameterSpecification(
inputNode.getLine(),
inputNode.getColumn(),
positionalParameterCount++
if ( numberNode == null ) {
throw new QueryException(
String.format(
Locale.ROOT,
ERROR_LEGACY_ORDINAL_PARAMS_NO_LONGER_SUPPORTED,
queryTranslatorImpl.getQueryString()
)
);
}
final String positionString = numberNode.getText();
final int label = Integer.parseInt( positionString );
trackPositionalParameterPositions( label );
final ParameterNode parameter = (ParameterNode) astFactory.create( PARAM, positionString );
parameter.setText( "?" );
final int queryParamtersPosition = isFilter()
? label
: label - 1;
final PositionalParameterSpecification paramSpec = new PositionalParameterSpecification(
delimiterNode.getLine(),
delimiterNode.getColumn(),
label,
queryParamtersPosition
);
parameter.setHqlParameterSpecification( paramSpec );
parameters.add( paramSpec );
parameterSpecs.add( paramSpec );
return parameter;
}
@SuppressWarnings("unchecked")
private void trackPositionalParameterPositions(int label) {
if ( positionalParameters == null ) {
positionalParameters = new HashMap();
}
final Integer loc = parameterCount++;
final Object existingValue = positionalParameters.get( label );
if ( existingValue == null ) {
positionalParameters.put( label, loc );
}
else if ( existingValue instanceof Integer ) {
final ArrayList list = new ArrayList();
positionalParameters.put( label, list );
list.add( existingValue );
list.add( loc );
}
else {
( (List) existingValue ).add( loc );
}
}
@Override
protected AST generateNamedParameter(AST delimiterNode, AST nameNode) throws SemanticException {
String name = nameNode.getText();
final String name = nameNode.getText();
trackNamedParameterPositions( name );
// create the node initially with the param name so that it shows
// appropriately in the "original text" attribute
ParameterNode parameter = (ParameterNode) astFactory.create( NAMED_PARAM, name );
final ParameterNode parameter = (ParameterNode) astFactory.create( NAMED_PARAM, name );
parameter.setText( "?" );
NamedParameterSpecification paramSpec = new NamedParameterSpecification(
final NamedParameterSpecification paramSpec = new NamedParameterSpecification(
delimiterNode.getLine(),
delimiterNode.getColumn(),
name
);
parameter.setHqlParameterSpecification( paramSpec );
parameters.add( paramSpec );
parameterSpecs.add( paramSpec );
return parameter;
}
@SuppressWarnings("unchecked")
private void trackNamedParameterPositions(String name) {
Integer loc = parameterCount++;
Object o = namedParameters.get( name );
if ( o == null ) {
if ( namedParameters == null ) {
namedParameters = new HashMap();
}
final Integer loc = parameterCount++;
final Object existingValue = namedParameters.get( name );
if ( existingValue == null ) {
namedParameters.put( name, loc );
}
else if ( o instanceof Integer ) {
ArrayList list = new ArrayList( 4 );
list.add( o );
else if ( existingValue instanceof Integer ) {
ArrayList<Integer> list = new ArrayList<>( 4 );
list.add( (Integer) existingValue );
list.add( loc );
namedParameters.put( name, list );
}
else {
( (ArrayList) o ).add( loc );
( (List) existingValue ).add( loc );
}
}
@ -1284,8 +1335,8 @@ public class HqlSqlWalker extends HqlSqlBaseWalker implements ErrorReporter, Par
return printer;
}
public ArrayList<ParameterSpecification> getParameters() {
return parameters;
public ArrayList<ParameterSpecification> getParameterSpecs() {
return parameterSpecs;
}
public int getNumberOfParametersInSetClause() {
@ -1356,7 +1407,7 @@ public class HqlSqlWalker extends HqlSqlBaseWalker implements ErrorReporter, Par
versionIncrementNode = getASTFactory().create( HqlSqlTokenTypes.PARAM, "?" );
ParameterSpecification paramSpec = new VersionTypeSeedParameterSpecification( versionType );
( (ParameterNode) versionIncrementNode ).setHqlParameterSpecification( paramSpec );
parameters.add( 0, paramSpec );
parameterSpecs.add( 0, paramSpec );
}
else {
// Not possible to simply re-use the versionPropertyNode here as it causes

View File

@ -0,0 +1,53 @@
/*
* 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.hql.internal.ast;
import java.util.ArrayList;
import java.util.List;
import org.hibernate.hql.spi.NamedParameterInformation;
import org.hibernate.internal.util.collections.ArrayHelper;
import org.hibernate.type.Type;
/**
* @author Steve Ebersole
*/
public class NamedParameterInformationImpl implements NamedParameterInformation {
private final String name;
private final List<Integer> sqlPositions = new ArrayList<>();
private Type expectedType;
NamedParameterInformationImpl(String name, Type initialType) {
this.name = name;
this.expectedType = initialType;
}
@Override
public String getSourceName() {
return name;
}
@Override
public int[] getSourceLocations() {
return ArrayHelper.toIntArray( sqlPositions );
}
@Override
public Type getExpectedType() {
return expectedType;
}
public void addSourceLocation(int position) {
sqlPositions.add( position );
}
public void setExpectedType(Type expectedType) {
this.expectedType = expectedType;
}
}

View File

@ -6,19 +6,17 @@
*/
package org.hibernate.hql.internal.ast;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.hibernate.hql.spi.NamedParameterInformation;
import org.hibernate.hql.spi.ParameterTranslations;
import org.hibernate.internal.util.collections.ArrayHelper;
import org.hibernate.hql.spi.PositionalParameterInformation;
import org.hibernate.param.NamedParameterSpecification;
import org.hibernate.param.ParameterSpecification;
import org.hibernate.param.PositionalParameterSpecification;
import org.hibernate.type.Type;
/**
* Defines the information available for parameters encountered during
@ -27,52 +25,8 @@ import org.hibernate.type.Type;
* @author Steve Ebersole
*/
public class ParameterTranslationsImpl implements ParameterTranslations {
private final Map<String,ParameterInfo> namedParameters;
private final ParameterInfo[] ordinalParameters;
@Override
public boolean supportsOrdinalParameterMetadata() {
return true;
}
@Override
public int getOrdinalParameterCount() {
return ordinalParameters.length;
}
public ParameterInfo getOrdinalParameterInfo(int ordinalPosition) {
return ordinalParameters[ordinalPosition];
}
@Override
public int getOrdinalParameterSqlLocation(int ordinalPosition) {
return getOrdinalParameterInfo( ordinalPosition ).getSqlLocations()[0];
}
@Override
public Type getOrdinalParameterExpectedType(int ordinalPosition) {
return getOrdinalParameterInfo( ordinalPosition ).getExpectedType();
}
@Override
public Set getNamedParameterNames() {
return namedParameters.keySet();
}
public ParameterInfo getNamedParameterInfo(String name) {
return namedParameters.get( name );
}
@Override
public int[] getNamedParameterSqlLocations(String name) {
return getNamedParameterInfo( name ).getSqlLocations();
}
@Override
public Type getNamedParameterExpectedType(String name) {
return getNamedParameterInfo( name ).getExpectedType();
}
private final Map<String,NamedParameterInformationImpl> namedParameters;
private final Map<Integer,PositionalParameterInformationImpl > ordinalParameters;
/**
* Constructs a parameter metadata object given a list of parameter
* specifications.
@ -82,78 +36,72 @@ public class ParameterTranslationsImpl implements ParameterTranslations {
*
* @param parameterSpecifications The parameter specifications
*/
public ParameterTranslationsImpl(List<ParameterSpecification> parameterSpecifications) {
class NamedParamTempHolder {
private String name;
private Type type;
private List<Integer> positions = new ArrayList<>();
ParameterTranslationsImpl(List<ParameterSpecification> parameterSpecifications) {
Map<String, NamedParameterInformationImpl> namedParameters = null;
Map<Integer, PositionalParameterInformationImpl> ordinalParameters = null;
int i = 0;
for ( ParameterSpecification specification : parameterSpecifications ) {
if ( PositionalParameterSpecification.class.isInstance( specification ) ) {
if ( ordinalParameters == null ) {
ordinalParameters = new HashMap<>();
}
final PositionalParameterSpecification ordinalSpecification = (PositionalParameterSpecification) specification;
final PositionalParameterInformationImpl info = ordinalParameters.computeIfAbsent(
ordinalSpecification.getLabel(),
k -> new PositionalParameterInformationImpl( k, ordinalSpecification.getExpectedType() )
);
info.addSourceLocation( i++ );
}
else if ( NamedParameterSpecification.class.isInstance( specification ) ) {
if ( namedParameters == null ) {
namedParameters = new HashMap<>();
}
final NamedParameterSpecification namedSpecification = (NamedParameterSpecification) specification;
final NamedParameterInformationImpl info = namedParameters.computeIfAbsent(
namedSpecification.getName(),
k -> new NamedParameterInformationImpl( k, namedSpecification.getExpectedType() )
);
info.addSourceLocation( i++ );
}
}
final int size = parameterSpecifications.size();
final List<ParameterInfo> ordinalParameterList = new ArrayList<>();
final Map<String,NamedParamTempHolder> namedParameterMap = new HashMap<>();
for ( int i = 0; i < size; i++ ) {
final ParameterSpecification spec = parameterSpecifications.get( i );
if ( PositionalParameterSpecification.class.isInstance( spec ) ) {
final PositionalParameterSpecification ordinalSpec = (PositionalParameterSpecification) spec;
ordinalParameterList.add( new ParameterInfo( i, ordinalSpec.getExpectedType() ) );
}
else if ( NamedParameterSpecification.class.isInstance( spec ) ) {
final NamedParameterSpecification namedSpec = (NamedParameterSpecification) spec;
NamedParamTempHolder paramHolder = namedParameterMap.get( namedSpec.getName() );
if ( paramHolder == null ) {
paramHolder = new NamedParamTempHolder();
paramHolder.name = namedSpec.getName();
paramHolder.type = namedSpec.getExpectedType();
namedParameterMap.put( namedSpec.getName(), paramHolder );
}
else if ( paramHolder.type == null && namedSpec.getExpectedType() != null ) {
// previous reference to the named parameter did not have type determined;
// this time, it can be determined by namedSpec.getExpectedType().
paramHolder.type = namedSpec.getExpectedType();
}
paramHolder.positions.add( i );
}
// don't care about other param types here, just those explicitly user-defined...
}
ordinalParameters = ordinalParameterList.toArray( new ParameterInfo[ordinalParameterList.size()] );
if ( namedParameterMap.isEmpty() ) {
namedParameters = java.util.Collections.emptyMap();
if ( namedParameters == null ) {
this.namedParameters = Collections.emptyMap();
}
else {
final Map<String,ParameterInfo> namedParametersBacking = new HashMap<>( namedParameterMap.size() );
for ( NamedParamTempHolder holder : namedParameterMap.values() ) {
namedParametersBacking.put(
holder.name,
new ParameterInfo( ArrayHelper.toIntArray( holder.positions ), holder.type )
);
}
namedParameters = java.util.Collections.unmodifiableMap( namedParametersBacking );
this.namedParameters = Collections.unmodifiableMap( namedParameters );
}
if ( ordinalParameters == null ) {
this.ordinalParameters = Collections.emptyMap();
}
else {
this.ordinalParameters = Collections.unmodifiableMap( ordinalParameters );
}
}
public static class ParameterInfo implements Serializable {
private final int[] sqlLocations;
private final Type expectedType;
@Override
@SuppressWarnings("unchecked")
public Map getNamedParameterInformationMap() {
return namedParameters;
}
public ParameterInfo(int[] sqlPositions, Type expectedType) {
this.sqlLocations = sqlPositions;
this.expectedType = expectedType;
}
@SuppressWarnings("unchecked")
@Override
public Map getPositionalParameterInformationMap() {
return ordinalParameters;
}
public ParameterInfo(int sqlPosition, Type expectedType) {
this.sqlLocations = new int[] { sqlPosition };
this.expectedType = expectedType;
}
@Override
public PositionalParameterInformation getPositionalParameterInformation(int position) {
return ordinalParameters.get( position );
}
public int[] getSqlLocations() {
return sqlLocations;
}
public Type getExpectedType() {
return expectedType;
}
@Override
public NamedParameterInformation getNamedParameterInformation(String name) {
return namedParameters.get( name );
}
}

View File

@ -0,0 +1,55 @@
/*
* 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.hql.internal.ast;
import java.util.ArrayList;
import java.util.List;
import org.hibernate.hql.spi.PositionalParameterInformation;
import org.hibernate.internal.util.collections.ArrayHelper;
import org.hibernate.type.Type;
/**
* @author Steve Ebersole
*/
public class PositionalParameterInformationImpl implements PositionalParameterInformation {
private final int label;
private final List<Integer> sourceLocations = new ArrayList<>();
private Type expectedType;
public PositionalParameterInformationImpl(int label, Type initialType) {
this.label = label;
this.expectedType = initialType;
}
@Override
public int getLabel() {
return label;
}
@Override
public int[] getSourceLocations() {
return ArrayHelper.toIntArray( sourceLocations );
}
@Override
public Type getExpectedType() {
return expectedType;
}
@Override
public void setExpectedType(Type expectedType) {
this.expectedType = expectedType;
}
@Override
public void addSourceLocation(int location) {
sourceLocations.add( location );
}
}

View File

@ -48,6 +48,7 @@ import org.hibernate.internal.util.ReflectHelper;
import org.hibernate.internal.util.StringHelper;
import org.hibernate.internal.util.collections.IdentitySet;
import org.hibernate.loader.hql.QueryLoader;
import org.hibernate.param.CollectionFilterKeyParameterSpecification;
import org.hibernate.param.ParameterSpecification;
import org.hibernate.persister.entity.Queryable;
import org.hibernate.query.spi.ScrollableResultsImplementor;
@ -252,7 +253,12 @@ public class QueryTranslatorImpl implements FilterTranslator {
LOG.debugf( "SQL: %s", sql );
}
gen.getParseErrorHandler().throwQueryException();
collectedParameterSpecifications = gen.getCollectedParameters();
if ( collectedParameterSpecifications == null ) {
collectedParameterSpecifications = gen.getCollectedParameters();
}
else {
collectedParameterSpecifications.addAll( gen.getCollectedParameters() );
}
}
}
@ -583,7 +589,7 @@ public class QueryTranslatorImpl implements FilterTranslator {
@Override
public ParameterTranslations getParameterTranslations() {
if ( paramTranslations == null ) {
paramTranslations = new ParameterTranslationsImpl( getWalker().getParameters() );
paramTranslations = new ParameterTranslationsImpl( getWalker().getParameterSpecs() );
}
return paramTranslations;
}

View File

@ -158,7 +158,7 @@ public class SqlGenerator extends SqlGeneratorBase implements ErrorReporter {
public SqlGenerator(SessionFactoryImplementor sfi) {
super();
parseErrorHandler = new ErrorCounter();
parseErrorHandler = new ErrorTracker();
sessionFactory = sfi;
}

View File

@ -88,8 +88,7 @@ public class SyntheticAndFactory implements HqlSqlTokenTypes {
.getKeyType();
CollectionFilterKeyParameterSpecification paramSpec = new CollectionFilterKeyParameterSpecification(
hqlSqlWalker.getCollectionFilterRole(),
collectionFilterKeyType,
0
collectionFilterKeyType
);
fragment.addEmbeddedParameter( paramSpec );
}

View File

@ -0,0 +1,42 @@
/*
* 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.hql.internal.classic;
import java.util.ArrayList;
import java.util.List;
import org.hibernate.hql.spi.ParameterInformation;
import org.hibernate.internal.util.collections.ArrayHelper;
import org.hibernate.param.ParameterBinder;
import org.hibernate.type.Type;
/**
* @author Steve Ebersole
*/
public abstract class AbstractParameterInformation implements ParameterInformation, ParameterBinder {
private List<Integer> sqlPositions = new ArrayList<>();
@Override
public int[] getSourceLocations() {
return ArrayHelper.toIntArray( sqlPositions );
}
public void addSourceLocation(int position) {
sqlPositions.add( position );
}
@Override
public Type getExpectedType() {
// the classic translator does not know this information
return null;
}
@Override
public void setExpectedType(Type expectedType) {
// nothing to do - classic translator does not know this information
}
}

View File

@ -0,0 +1,42 @@
/*
* 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.hql.internal.classic;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import org.hibernate.engine.spi.QueryParameters;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.engine.spi.TypedValue;
import org.hibernate.hql.spi.NamedParameterInformation;
/**
* @author Steve Ebersole
*/
public class NamedParameterInformationImpl extends AbstractParameterInformation implements NamedParameterInformation {
private final String name;
NamedParameterInformationImpl(String name) {
this.name = name;
}
@Override
public String getSourceName() {
return name;
}
@Override
public int bind(
PreparedStatement statement,
QueryParameters qp,
SharedSessionContractImplementor session,
int position) throws SQLException {
final TypedValue typedValue = qp.getNamedParameters().get( name );
typedValue.getType().nullSafeSet( statement, typedValue.getValue(), position, session );
return typedValue.getType().getColumnSpan( session.getFactory() );
}
}

View File

@ -5,9 +5,13 @@
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.hql.internal.classic;
import java.util.Locale;
import org.hibernate.QueryException;
import org.hibernate.internal.util.StringHelper;
import static org.hibernate.hql.spi.QueryTranslator.ERROR_LEGACY_ORDINAL_PARAMS_NO_LONGER_SUPPORTED;
/**
* Parses the ORDER BY clause of a query
*/
@ -40,6 +44,29 @@ public class OrderByParser implements Parser {
q.addNamedParameter( token.substring( 1 ) );
q.appendOrderByToken( "?" );
}
else if ( token.startsWith( "?" ) ) {
// ordinal query parameter
if ( token.length() == 1 ) {
throw new QueryException(
String.format(
Locale.ROOT,
ERROR_LEGACY_ORDINAL_PARAMS_NO_LONGER_SUPPORTED,
q.getQueryString()
)
);
}
else {
final String labelString = token.substring( 1 );
try {
final int label = Integer.parseInt( labelString );
q.addOrdinalParameter( label );
q.appendOrderByToken( "?" );
}
catch (NumberFormatException e) {
throw new QueryException( "Ordinal parameter label must be numeric : " + labelString, e );
}
}
}
else {
q.appendOrderByToken( token );
}

View File

@ -0,0 +1,44 @@
/*
* 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.hql.internal.classic;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import org.hibernate.engine.spi.QueryParameters;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.engine.spi.TypedValue;
import org.hibernate.hql.spi.PositionalParameterInformation;
/**
* @author Steve Ebersole
*/
public class PositionalParameterInformationImpl
extends AbstractParameterInformation
implements PositionalParameterInformation {
private final int label;
public PositionalParameterInformationImpl(int label) {
this.label = label;
}
@Override
public int getLabel() {
return label;
}
@Override
public int bind(
PreparedStatement statement,
QueryParameters qp,
SharedSessionContractImplementor session,
int position) throws SQLException {
final TypedValue typedValue = qp.getNamedParameters().get( Integer.toString( label ) );
typedValue.getType().nullSafeSet( statement, typedValue.getValue(), position, session );
return typedValue.getType().getColumnSpan( session.getFactory() );
}
}

View File

@ -38,7 +38,9 @@ import org.hibernate.event.spi.EventSource;
import org.hibernate.hql.internal.HolderInstantiator;
import org.hibernate.hql.internal.NameGenerator;
import org.hibernate.hql.spi.FilterTranslator;
import org.hibernate.hql.spi.NamedParameterInformation;
import org.hibernate.hql.spi.ParameterTranslations;
import org.hibernate.hql.spi.PositionalParameterInformation;
import org.hibernate.internal.CoreLogging;
import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.internal.IteratorImpl;
@ -47,6 +49,8 @@ import org.hibernate.internal.util.StringHelper;
import org.hibernate.internal.util.collections.ArrayHelper;
import org.hibernate.loader.BasicLoader;
import org.hibernate.loader.spi.AfterLoadAction;
import org.hibernate.param.CollectionFilterKeyParameterSpecification;
import org.hibernate.param.ParameterBinder;
import org.hibernate.persister.collection.CollectionPersister;
import org.hibernate.persister.collection.QueryableCollection;
import org.hibernate.persister.entity.Loadable;
@ -78,12 +82,15 @@ public class QueryTranslatorImpl extends BasicLoader implements FilterTranslator
private List returnedTypes = new ArrayList();
private final List fromTypes = new ArrayList();
private final List scalarTypes = new ArrayList();
private final Map namedParameters = new HashMap();
private final Map aliasNames = new HashMap();
private final Map oneToOneOwnerNames = new HashMap();
private final Map uniqueKeyOwnerReferences = new HashMap();
private final Map decoratedPropertyMappings = new HashMap();
private final Map<String,NamedParameterInformationImpl> namedParameters = new HashMap<>();
private final Map<Integer, PositionalParameterInformationImpl> ordinalParameters = new HashMap<>();
private final List<ParameterBinder> paramValueBinders = new ArrayList<>();
private final List scalarSelectTokens = new ArrayList();
private final List whereTokens = new ArrayList();
private final List havingTokens = new ArrayList();
@ -205,6 +212,12 @@ public class QueryTranslatorImpl extends BasicLoader implements FilterTranslator
if ( !isCompiled() ) {
addFromAssociation( "this", collectionRole );
paramValueBinders.add(
new CollectionFilterKeyParameterSpecification(
collectionRole,
getFactory().getMetamodel().collectionPersister( collectionRole ).getKeyType()
)
);
compile( replacements, scalar );
}
}
@ -530,20 +543,81 @@ public class QueryTranslatorImpl extends BasicLoader implements FilterTranslator
if ( superQuery != null ) {
superQuery.addNamedParameter( name );
}
Integer loc = parameterCount++;
Object o = namedParameters.get( name );
if ( o == null ) {
namedParameters.put( name, loc );
final Integer loc = parameterCount++;
final NamedParameterInformationImpl info = namedParameters.computeIfAbsent(
name,
k -> new NamedParameterInformationImpl( name )
);
paramValueBinders.add( info );
info.addSourceLocation( loc );
}
private enum OrdinalParameterStyle { LABELED, LEGACY }
private OrdinalParameterStyle ordinalParameterStyle;
private int legacyPositionalParameterCount = 0;
void addLegacyPositionalParameter() {
if ( superQuery != null ) {
superQuery.addLegacyPositionalParameter();
}
else if ( o instanceof Integer ) {
ArrayList list = new ArrayList( 4 );
list.add( o );
list.add( loc );
namedParameters.put( name, list );
if ( ordinalParameterStyle == null ) {
ordinalParameterStyle = OrdinalParameterStyle.LEGACY;
}
else {
( (ArrayList) o ).add( loc );
else if ( ordinalParameterStyle != OrdinalParameterStyle.LEGACY ) {
throw new QueryException( "Cannot mix legacy and labeled positional parameters" );
}
final Integer label = legacyPositionalParameterCount++;
final PositionalParameterInformationImpl paramInfo = new PositionalParameterInformationImpl( label );
ordinalParameters.put( label, paramInfo );
paramValueBinders.add( paramInfo );
final Integer loc = parameterCount++;
paramInfo.addSourceLocation( loc );
}
void addOrdinalParameter(int label) {
if ( superQuery != null ) {
superQuery.addOrdinalParameter( label );
}
if ( ordinalParameterStyle == null ) {
ordinalParameterStyle = OrdinalParameterStyle.LABELED;
}
else if ( ordinalParameterStyle != OrdinalParameterStyle.LABELED ) {
throw new QueryException( "Cannot mix legacy and labeled positional parameters" );
}
final Integer loc = parameterCount++;
final PositionalParameterInformationImpl info = ordinalParameters.computeIfAbsent(
label,
k -> new PositionalParameterInformationImpl( label )
);
paramValueBinders.add( info );
info.addSourceLocation( loc );
}
@Override
protected int bindParameterValues(
PreparedStatement statement,
QueryParameters queryParameters,
int startIndex,
SharedSessionContractImplementor session) throws SQLException {
int span = 0;
for ( ParameterBinder binder : paramValueBinders ) {
span += binder.bind( statement, queryParameters, session, startIndex + span );
}
return span;
}
@Override
@ -561,7 +635,6 @@ public class QueryTranslatorImpl extends BasicLoader implements FilterTranslator
}
private void renderSQL() throws QueryException, MappingException {
final int rtsize;
if ( returnedTypes.size() == 0 && scalarTypes.size() == 0 ) {
//ie no select clause in HQL
@ -973,7 +1046,7 @@ public class QueryTranslatorImpl extends BasicLoader implements FilterTranslator
}
try {
final List<AfterLoadAction> afterLoadActions = new ArrayList<AfterLoadAction>();
final List<AfterLoadAction> afterLoadActions = new ArrayList<>();
final SqlStatementWrapper wrapper = executeQueryStatement(
queryParameters,
false,
@ -1267,40 +1340,25 @@ public class QueryTranslatorImpl extends BasicLoader implements FilterTranslator
public ParameterTranslations getParameterTranslations() {
return new ParameterTranslations() {
@Override
public boolean supportsOrdinalParameterMetadata() {
// classic translator does not support collection of ordinal
// param metadata
return false;
@SuppressWarnings("unchecked")
public Map getNamedParameterInformationMap() {
return namedParameters;
}
@Override
public int getOrdinalParameterCount() {
return 0; // not known!
@SuppressWarnings("unchecked")
public Map getPositionalParameterInformationMap() {
return ordinalParameters;
}
@Override
public int getOrdinalParameterSqlLocation(int ordinalPosition) {
return 0; // not known!
public PositionalParameterInformation getPositionalParameterInformation(int position) {
return ordinalParameters.get( position );
}
@Override
public Type getOrdinalParameterExpectedType(int ordinalPosition) {
return null; // not known!
}
@Override
public Set getNamedParameterNames() {
return namedParameters.keySet();
}
@Override
public int[] getNamedParameterSqlLocations(String name) {
return getNamedParameterLocs( name );
}
@Override
public Type getNamedParameterExpectedType(String name) {
return null; // not known!
public NamedParameterInformation getNamedParameterInformation(String name) {
return namedParameters.get( name );
}
};
}

View File

@ -27,6 +27,8 @@ import org.hibernate.type.EntityType;
import org.hibernate.type.LiteralType;
import org.hibernate.type.Type;
import static org.hibernate.hql.spi.QueryTranslator.ERROR_LEGACY_ORDINAL_PARAMS_NO_LONGER_SUPPORTED;
/**
* Parses the where clause of a hibernate query and translates it to an
* SQL where clause.
@ -396,13 +398,33 @@ public class WhereParser implements Parser {
}
private void doToken(String token, QueryTranslatorImpl q) throws QueryException {
if ( q.isName( StringHelper.root( token ) ) ) { //path expression
if ( q.isName( StringHelper.root( token ) ) ) {
//path expression
doPathExpression( q.unalias( token ), q );
}
else if ( token.startsWith( ParserHelper.HQL_VARIABLE_PREFIX ) ) { //named query parameter
else if ( token.startsWith( ParserHelper.HQL_VARIABLE_PREFIX ) ) {
//named query parameter
q.addNamedParameter( token.substring( 1 ) );
appendToken( q, "?" );
}
else if ( token.startsWith( "?" ) ) {
// ordinal query parameter
if ( token.length() == 1 ) {
q.addLegacyPositionalParameter();
appendToken( q, "?" );
}
else {
final String labelString = token.substring( 1 );
try {
final int label = Integer.parseInt( labelString );
q.addOrdinalParameter( label );
appendToken( q, "?" );
}
catch (NumberFormatException e) {
throw new QueryException( "Ordinal parameter label must be numeric : " + labelString, e );
}
}
}
else {
Queryable persister = q.getEntityPersisterUsingImports( token );
if ( persister != null ) { // the name of a class

View File

@ -0,0 +1,14 @@
/*
* 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.hql.spi;
/**
* @author Steve Ebersole
*/
public interface NamedParameterInformation extends ParameterInformation {
String getSourceName();
}

View File

@ -0,0 +1,28 @@
/*
* 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.hql.spi;
import org.hibernate.type.Type;
/**
* @author Steve Ebersole
*/
public interface ParameterInformation {
/**
* The positions (relative to all parameters) that this parameter
* was discovered in the source query (HQL, etc). E.g., given a query
* like `.. where a.name = :name or a.nickName = :name` this would
* return `[0,1]`
*/
int[] getSourceLocations();
Type getExpectedType();
void setExpectedType(Type expectedType);
void addSourceLocation(int position);
}

View File

@ -5,9 +5,8 @@
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.hql.spi;
import java.util.Set;
import org.hibernate.type.Type;
import java.util.Map;
/**
* Defines available information about the parameters encountered during
@ -16,18 +15,10 @@ import org.hibernate.type.Type;
* @author Steve Ebersole
*/
public interface ParameterTranslations {
Map<String,NamedParameterInformation> getNamedParameterInformationMap();
Map<Integer,PositionalParameterInformation> getPositionalParameterInformationMap();
public boolean supportsOrdinalParameterMetadata();
PositionalParameterInformation getPositionalParameterInformation(int position);
public int getOrdinalParameterCount();
public int getOrdinalParameterSqlLocation(int ordinalPosition);
public Type getOrdinalParameterExpectedType(int ordinalPosition);
public Set getNamedParameterNames();
public int[] getNamedParameterSqlLocations(String name);
public Type getNamedParameterExpectedType(String name);
NamedParameterInformation getNamedParameterInformation(String name);
}

View File

@ -0,0 +1,14 @@
/*
* 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.hql.spi;
/**
* @author Steve Ebersole
*/
public interface PositionalParameterInformation extends ParameterInformation {
int getLabel();
}

View File

@ -29,6 +29,8 @@ import org.hibernate.type.Type;
public interface QueryTranslator {
String ERROR_CANNOT_FETCH_WITH_ITERATE = "fetch may not be used with scroll() or iterate()";
String ERROR_NAMED_PARAMETER_DOES_NOT_APPEAR = "Named parameter does not appear in Query: ";
String ERROR_ORDINAL_PARAMETER_DOES_NOT_APPEAR = "Ordinal parameter [%s] does not appear in Query [%s] ";
String ERROR_LEGACY_ORDINAL_PARAMS_NO_LONGER_SUPPORTED = "Legacy-style query parameters (`?`) are no longer supported; use JPA-style ordinal parameters (e.g., `?1`) instead : %s";
String ERROR_CANNOT_DETERMINE_TYPE = "Could not determine type of: ";
String ERROR_CANNOT_FORMAT_LITERAL = "Could not format constant value to SQL literal: ";

View File

@ -746,9 +746,7 @@ public abstract class AbstractSharedSessionContract implements SharedSessionCont
@Override
public QueryImplementor createNamedQuery(String name) {
final QueryImplementor<Object> query = buildQueryFromName( name, null );
query.getParameterMetadata().setOrdinalParametersZeroBased( false );
return query;
return buildQueryFromName( name, null );
}
protected <T> QueryImplementor<T> buildQueryFromName(String name, Class<T> resultType) {
@ -872,9 +870,7 @@ public abstract class AbstractSharedSessionContract implements SharedSessionCont
@Override
public NativeQueryImplementor createNativeQuery(String sqlString) {
final NativeQueryImpl query = (NativeQueryImpl) getNativeQueryImplementor( sqlString, false );
query.setZeroBasedParametersIndex( false );
return query;
return getNativeQueryImplementor( sqlString, false );
}
@Override
@ -962,9 +958,7 @@ public abstract class AbstractSharedSessionContract implements SharedSessionCont
@Override
public NativeQueryImplementor getNamedSQLQuery(String name) {
final NativeQueryImpl nativeQuery = (NativeQueryImpl) getNamedNativeQuery( name );
nativeQuery.setZeroBasedParametersIndex( true );
return nativeQuery;
return getNamedNativeQuery( name );
}
@Override

View File

@ -96,6 +96,7 @@ import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.engine.spi.SessionOwner;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.engine.spi.Status;
import org.hibernate.engine.spi.TypedValue;
import org.hibernate.engine.transaction.spi.TransactionImplementor;
import org.hibernate.engine.transaction.spi.TransactionObserver;
import org.hibernate.event.service.spi.EventListenerGroup;
@ -153,6 +154,7 @@ import org.hibernate.loader.criteria.CriteriaLoader;
import org.hibernate.loader.custom.CustomLoader;
import org.hibernate.loader.custom.CustomQuery;
import org.hibernate.metamodel.spi.MetamodelImplementor;
import org.hibernate.param.CollectionFilterKeyParameterSpecification;
import org.hibernate.persister.collection.CollectionPersister;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.persister.entity.MultiLoadOptions;
@ -179,6 +181,7 @@ import org.hibernate.resource.transaction.spi.TransactionCoordinator;
import org.hibernate.resource.transaction.spi.TransactionStatus;
import org.hibernate.stat.SessionStatistics;
import org.hibernate.stat.internal.SessionStatisticsImpl;
import org.hibernate.type.Type;
import static org.hibernate.cfg.AvailableSettings.JPA_LOCK_SCOPE;
import static org.hibernate.cfg.AvailableSettings.JPA_LOCK_TIMEOUT;
@ -1755,8 +1758,13 @@ public final class SessionImpl
}
if ( parameters != null ) {
parameters.getPositionalParameterValues()[0] = entry.getLoadedKey();
parameters.getPositionalParameterTypes()[0] = entry.getLoadedPersister().getKeyType();
parameters.getNamedParameters().put(
CollectionFilterKeyParameterSpecification.PARAM_KEY,
new TypedValue(
entry.getLoadedPersister().getKeyType(),
entry.getLoadedKey()
)
);
}
return plan;

View File

@ -238,4 +238,13 @@ public interface DeprecationLogger extends BasicLogger {
"or [hibernate.connection.release_mode]; use [hibernate.connection.handling_mode] instead"
)
void logUseOfDeprecatedConnectionHandlingSettings();
@LogMessage(level = WARN)
@Message(
id = 90000024,
value = "Application requested zero be used as the base for JDBC-style parameters found in native-queries; " +
"this is a *temporary* backwards-compatibility setting to help applications using versions prior to " +
"5.3 in upgrading. It will be removed in a later version."
)
void logUseOfDeprecatedZeroBasedJdbcStyleParams();
}

View File

@ -29,7 +29,6 @@ import org.hibernate.LockOptions;
import org.hibernate.QueryException;
import org.hibernate.ScrollMode;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.StaleObjectStateException;
import org.hibernate.WrongClassException;
import org.hibernate.cache.spi.FilterKey;
@ -68,6 +67,7 @@ import org.hibernate.internal.ScrollableResultsImpl;
import org.hibernate.internal.util.StringHelper;
import org.hibernate.internal.util.collections.CollectionHelper;
import org.hibernate.loader.spi.AfterLoadAction;
import org.hibernate.param.ParameterBinder;
import org.hibernate.persister.collection.CollectionPersister;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.persister.entity.Loadable;

View File

@ -29,7 +29,9 @@ import org.hibernate.QueryException;
import org.hibernate.criterion.CriteriaQuery;
import org.hibernate.criterion.Criterion;
import org.hibernate.criterion.EnhancedProjection;
import org.hibernate.criterion.ParameterInfoCollector;
import org.hibernate.criterion.Projection;
import org.hibernate.engine.query.spi.OrdinalParameterDescriptor;
import org.hibernate.engine.spi.QueryParameters;
import org.hibernate.engine.spi.RowSelection;
import org.hibernate.engine.spi.SessionFactoryImplementor;
@ -311,6 +313,7 @@ public class CriteriaQueryTranslator implements CriteriaQuery {
final List<Object> values = new ArrayList<Object>();
final List<Type> types = new ArrayList<Type>();
final Iterator<CriteriaImpl.Subcriteria> subcriteriaIterator = rootCriteria.iterateSubcriteria();
while ( subcriteriaIterator.hasNext() ) {
final CriteriaImpl.Subcriteria subcriteria = subcriteriaIterator.next();

View File

@ -7,12 +7,12 @@
package org.hibernate.loader.custom;
import java.io.Serializable;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.hibernate.HibernateException;
@ -33,6 +33,7 @@ import org.hibernate.loader.CollectionAliases;
import org.hibernate.loader.EntityAliases;
import org.hibernate.loader.Loader;
import org.hibernate.loader.spi.AfterLoadAction;
import org.hibernate.param.ParameterBinder;
import org.hibernate.persister.collection.CollectionPersister;
import org.hibernate.persister.collection.QueryableCollection;
import org.hibernate.persister.entity.Loadable;
@ -55,7 +56,8 @@ public class CustomLoader extends Loader {
private final String sql;
private final Set<Serializable> querySpaces = new HashSet<>();
private final Map namedParameterBindPoints;
private final List<ParameterBinder> paramValueBinders;
private final Queryable[] entityPersisters;
private final int[] entiytOwners;
@ -85,7 +87,8 @@ public class CustomLoader extends Loader {
this.sql = customQuery.getSQL();
this.querySpaces.addAll( customQuery.getQuerySpaces() );
this.namedParameterBindPoints = customQuery.getNamedParameterBindPoints();
this.paramValueBinders = customQuery.getParameterValueBinders();
List<Queryable> entityPersisters = new ArrayList<>();
List<Integer> entityOwners = new ArrayList<>();
@ -453,23 +456,32 @@ public class CustomLoader extends Loader {
}
@Override
public int[] getNamedParameterLocs(String name) throws QueryException {
Object loc = namedParameterBindPoints.get( name );
if ( loc == null ) {
throw new QueryException(
"Named parameter does not appear in Query: " + name,
sql
protected int bindParameterValues(
PreparedStatement statement,
QueryParameters queryParameters,
int startIndex,
SharedSessionContractImplementor session) throws SQLException {
final Serializable optionalId = queryParameters.getOptionalId();
if ( optionalId != null ) {
paramValueBinders.get( 0 ).bind( statement, queryParameters, session, startIndex );
return session.getFactory().getMetamodel()
.entityPersister( queryParameters.getOptionalEntityName() )
.getIdentifierType()
.getColumnSpan( session.getFactory() );
}
int span = 0;
for ( ParameterBinder paramValueBinder : paramValueBinders ) {
span += paramValueBinder.bind(
statement,
queryParameters,
session,
startIndex + span
);
}
if ( loc instanceof Integer ) {
return new int[] {(Integer) loc};
}
else {
return ArrayHelper.toIntArray( (List) loc );
}
return span;
}
@Override
protected void autoDiscoverTypes(ResultSet rs) {
try {

View File

@ -5,10 +5,12 @@
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.loader.custom;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.hibernate.param.ParameterBinder;
/**
* Extension point allowing any SQL query with named and positional parameters
* to be executed by Hibernate, returning managed entities, collections and
@ -23,7 +25,7 @@ public interface CustomQuery {
*
* @return The SQL statement string.
*/
public String getSQL();
String getSQL();
/**
* Any query spaces to apply to the query execution. Query spaces are
@ -32,22 +34,9 @@ public interface CustomQuery {
*
* @return The query spaces
*/
public Set<String> getQuerySpaces();
Set<String> getQuerySpaces();
/**
* A map representing positions within the supplied {@link #getSQL query} to
* which we need to bind named parameters.
* <p/>
* Optional, may return null if no named parameters.
* <p/>
* The structure of the returned map (if one) as follows:<ol>
* <li>The keys into the map are the named parameter names</li>
* <li>The corresponding value is either an {@link Integer} if the
* parameter occurs only once in the query; or a List of Integers if the
* parameter occurs more than once</li>
* </ol>
*/
public Map getNamedParameterBindPoints();
List<ParameterBinder> getParameterValueBinders();
/**
* A collection of {@link Return descriptors} describing the
@ -55,5 +44,6 @@ public interface CustomQuery {
*
* @return List of return descriptors.
*/
public List<Return> getCustomQueryReturns();
List<Return> getCustomQueryReturns();
}

View File

@ -0,0 +1,37 @@
/*
* 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.loader.custom.sql;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import org.hibernate.engine.spi.QueryParameters;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.engine.spi.TypedValue;
import org.hibernate.param.ParameterBinder;
/**
* @author Steve Ebersole
*/
public class NamedParamBinder implements ParameterBinder {
private final String name;
public NamedParamBinder(String name) {
this.name = name;
}
@Override
public int bind(
PreparedStatement statement,
QueryParameters qp,
SharedSessionContractImplementor session,
int position) throws SQLException {
final TypedValue typedValue = qp.getNamedParameters().get( name );
typedValue.getType().nullSafeSet( statement, typedValue.getValue(), position, session );
return typedValue.getType().getColumnSpan( session.getFactory() );
}
}

View File

@ -0,0 +1,37 @@
/*
* 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.loader.custom.sql;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import org.hibernate.engine.spi.QueryParameters;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.engine.spi.TypedValue;
import org.hibernate.param.ParameterBinder;
/**
* @author Steve Ebersole
*/
public class PositionalParamBinder implements ParameterBinder {
private final int label;
public PositionalParamBinder(int label) {
this.label = label;
}
@Override
public int bind(
PreparedStatement statement,
QueryParameters qp,
SharedSessionContractImplementor session,
int position) throws SQLException {
final TypedValue typedValue = qp.getNamedParameters().get( Integer.toString( label ) );
typedValue.getType().nullSafeSet( statement, typedValue.getValue(), position, session );
return typedValue.getType().getColumnSpan( session.getFactory() );
}
}

View File

@ -9,7 +9,6 @@ package org.hibernate.loader.custom.sql;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
@ -20,6 +19,7 @@ import org.hibernate.engine.query.spi.sql.NativeSQLQueryReturn;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.loader.custom.CustomQuery;
import org.hibernate.param.ParameterBinder;
import org.hibernate.persister.collection.SQLLoadableCollection;
import org.hibernate.persister.entity.SQLLoadable;
@ -40,7 +40,9 @@ public class SQLCustomQuery implements CustomQuery, Serializable {
private final String sql;
private final Set querySpaces = new HashSet();
private final Map namedParameterBindPoints = new HashMap();
private final List<ParameterBinder> paramValueBinders;
private final List customQueryReturns = new ArrayList();
@ -52,8 +54,9 @@ public class SQLCustomQuery implements CustomQuery, Serializable {
return querySpaces;
}
public Map getNamedParameterBindPoints() {
return namedParameterBindPoints;
@Override
public List<ParameterBinder > getParameterValueBinders() {
return paramValueBinders;
}
public List getCustomQueryReturns() {
@ -115,7 +118,8 @@ public class SQLCustomQuery implements CustomQuery, Serializable {
SQLQueryParser parser = new SQLQueryParser( sqlQuery, new ParserContext( aliasContext ), factory );
this.sql = parser.process();
this.namedParameterBindPoints.putAll( parser.getNamedParameters() );
this.paramValueBinders = parser.getParameterValueBinders();
// SQLQueryParser parser = new SQLQueryParser(
// sqlQuery,

View File

@ -7,7 +7,7 @@
package org.hibernate.loader.custom.sql;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;
@ -15,6 +15,7 @@ import java.util.regex.Pattern;
import org.hibernate.QueryException;
import org.hibernate.engine.query.spi.ParameterParser;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.param.ParameterBinder;
import org.hibernate.persister.collection.SQLLoadableCollection;
import org.hibernate.persister.entity.SQLLoadable;
@ -35,9 +36,10 @@ public class SQLQueryParser {
private final String originalQueryString;
private final ParserContext context;
private final Map namedParameters = new HashMap();
private long aliasesFound;
private List<ParameterBinder> paramValueBinders;
interface ParserContext {
boolean isEntityAlias(String aliasName);
SQLLoadable getEntityPersisterByAlias(String alias);
@ -54,8 +56,8 @@ public class SQLQueryParser {
this.factory = factory;
}
public Map getNamedParameters() {
return namedParameters;
public List<ParameterBinder> getParameterValueBinders() {
return paramValueBinders == null ? Collections.emptyList() : paramValueBinders;
}
public boolean queryHasAliases() {
@ -276,19 +278,25 @@ public class SQLQueryParser {
* @return The SQL query with parameter substitution complete.
*/
private String substituteParams(String sqlString) {
ParameterSubstitutionRecognizer recognizer = new ParameterSubstitutionRecognizer();
final ParameterSubstitutionRecognizer recognizer = new ParameterSubstitutionRecognizer( factory );
ParameterParser.parse( sqlString, recognizer );
namedParameters.clear();
namedParameters.putAll( recognizer.namedParameterBindPoints );
paramValueBinders = recognizer.getParameterValueBinders();
return recognizer.result.toString();
}
public static class ParameterSubstitutionRecognizer implements ParameterParser.Recognizer {
StringBuilder result = new StringBuilder();
Map namedParameterBindPoints = new HashMap();
int parameterCount;
int jdbcPositionalParamCount;
private List<ParameterBinder> paramValueBinders;
public ParameterSubstitutionRecognizer(SessionFactoryImplementor factory) {
this.jdbcPositionalParamCount = factory.getSessionFactoryOptions().jdbcStyleParamsZeroBased()
? 0
: 1;
}
@Override
public void outParameter(int position) {
@ -298,17 +306,35 @@ public class SQLQueryParser {
@Override
public void ordinalParameter(int position) {
result.append( '?' );
registerPositionParamBinder( jdbcPositionalParamCount++ );
}
private void registerPositionParamBinder(int label) {
if ( paramValueBinders == null ) {
paramValueBinders = new ArrayList<>();
}
paramValueBinders.add( new PositionalParamBinder( label ) );
}
@Override
public void jpaPositionalParameter(int name, int position) {
result.append( '?' );
registerPositionParamBinder( name );
}
@Override
public void namedParameter(String name, int position) {
addNamedParameter( name );
result.append( '?' );
registerNamedParamBinder( name );
}
@Override
public void jpaPositionalParameter(String name, int position) {
namedParameter( name, position );
private void registerNamedParamBinder(String name) {
if ( paramValueBinders == null ) {
paramValueBinders = new ArrayList<>();
}
paramValueBinders.add( new NamedParamBinder( name ) );
}
@Override
@ -316,21 +342,12 @@ public class SQLQueryParser {
result.append( character );
}
private void addNamedParameter(String name) {
Integer loc = parameterCount++;
Object o = namedParameterBindPoints.get( name );
if ( o == null ) {
namedParameterBindPoints.put( name, loc );
}
else if ( o instanceof Integer ) {
ArrayList list = new ArrayList( 4 );
list.add( o );
list.add( loc );
namedParameterBindPoints.put( name, list );
}
else {
( ( List ) o ).add( loc );
}
public List<ParameterBinder> getParameterValueBinders() {
return paramValueBinders;
}
@Override
public void complete() {
}
}
}

View File

@ -9,14 +9,17 @@ package org.hibernate.loader.entity;
import java.io.Serializable;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Collections;
import java.util.List;
import org.hibernate.HibernateException;
import org.hibernate.LockOptions;
import org.hibernate.engine.spi.LoadQueryInfluencers;
import org.hibernate.engine.spi.QueryParameters;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.loader.OuterJoinLoader;
import org.hibernate.param.ParameterBinder;
import org.hibernate.persister.entity.OuterJoinLoadable;
import org.hibernate.transform.ResultTransformer;
import org.hibernate.type.Type;

View File

@ -32,6 +32,8 @@ import org.hibernate.hql.internal.ast.tree.AggregatedSelectExpression;
import org.hibernate.hql.internal.ast.tree.FromElement;
import org.hibernate.hql.internal.ast.tree.QueryNode;
import org.hibernate.hql.internal.ast.tree.SelectClause;
import org.hibernate.hql.spi.NamedParameterInformation;
import org.hibernate.hql.spi.ParameterInformation;
import org.hibernate.internal.IteratorImpl;
import org.hibernate.internal.util.collections.ArrayHelper;
import org.hibernate.loader.BasicLoader;
@ -599,7 +601,22 @@ public class QueryLoader extends BasicLoader {
*/
@Override
public int[] getNamedParameterLocs(String name) throws QueryException {
return queryTranslator.getParameterTranslations().getNamedParameterSqlLocations( name );
ParameterInformation info = queryTranslator.getParameterTranslations().getNamedParameterInformation( name );
if ( info == null ) {
try {
info = queryTranslator.getParameterTranslations().getPositionalParameterInformation(
Integer.parseInt( name )
);
}
catch (Exception ignore) {
}
}
if ( info == null ) {
throw new QueryException( "Unrecognized parameter label : " + name );
}
return info.getSourceLocations();
}
/**

View File

@ -15,8 +15,6 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import org.jboss.logging.Logger;
import org.hibernate.LockMode;
import org.hibernate.engine.spi.EntityKey;
import org.hibernate.engine.spi.QueryParameters;
@ -30,10 +28,11 @@ import org.hibernate.loader.plan.spi.EntityFetch;
import org.hibernate.loader.plan.spi.EntityReference;
import org.hibernate.loader.plan.spi.Fetch;
import org.hibernate.loader.plan.spi.LoadPlan;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.persister.entity.Loadable;
import org.hibernate.type.EntityType;
import org.jboss.logging.Logger;
/**
* @author Steve Ebersole
*/

View File

@ -6,9 +6,11 @@
*/
package org.hibernate.param;
import java.io.Serializable;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import org.hibernate.QueryException;
import org.hibernate.engine.spi.QueryParameters;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.type.Type;
@ -20,22 +22,20 @@ import org.hibernate.type.Type;
* @author Steve Ebersole
*/
public class CollectionFilterKeyParameterSpecification implements ParameterSpecification {
public static final String PARAM_KEY = "{collection_key}";
private final String collectionRole;
private final Type keyType;
private final int queryParameterPosition;
/**
* Creates a specialized collection-filter collection-key parameter spec.
*
* @param collectionRole The collection role being filtered.
* @param keyType The mapped collection-key type.
* @param queryParameterPosition The position within {@link org.hibernate.engine.spi.QueryParameters} where
* we can find the appropriate param value to bind.
*/
public CollectionFilterKeyParameterSpecification(String collectionRole, Type keyType, int queryParameterPosition) {
public CollectionFilterKeyParameterSpecification(String collectionRole, Type keyType) {
this.collectionRole = collectionRole;
this.keyType = keyType;
this.queryParameterPosition = queryParameterPosition;
}
@Override
@ -44,7 +44,7 @@ public class CollectionFilterKeyParameterSpecification implements ParameterSpeci
QueryParameters qp,
SharedSessionContractImplementor session,
int position) throws SQLException {
Object value = qp.getPositionalParameterValues()[queryParameterPosition];
final Object value = qp.getNamedParameters().get( PARAM_KEY ).getValue();
keyType.nullSafeSet( statement, value, position, session );
return keyType.getColumnSpan( session.getFactory() );
}

View File

@ -0,0 +1,32 @@
/*
* 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.param;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import org.hibernate.engine.spi.QueryParameters;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
/**
* @author Steve Ebersole
*/
public interface ParameterBinder {
/**
* Bind the appropriate value into the given statement at the specified position.
*
* @param statement The statement into which the value should be bound.
* @param qp The defined values for the current query execution.
* @param session The session against which the current execution is occuring.
* @param position The position from which to start binding value(s).
*
* @return The number of sql bind positions "eaten" by this bind operation.
* @throws java.sql.SQLException Indicates problems performing the JDBC biind operation.
*/
int bind(PreparedStatement statement, QueryParameters qp, SharedSessionContractImplementor session, int position) throws SQLException;
}

View File

@ -7,10 +7,7 @@
package org.hibernate.param;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import org.hibernate.engine.spi.QueryParameters;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.type.Type;
/**
@ -19,20 +16,7 @@ import org.hibernate.type.Type;
*
* @author Steve Ebersole
*/
public interface ParameterSpecification {
/**
* Bind the appropriate value into the given statement at the specified position.
*
* @param statement The statement into which the value should be bound.
* @param qp The defined values for the current query execution.
* @param session The session against which the current execution is occuring.
* @param position The position from which to start binding value(s).
*
* @return The number of sql bind positions "eaten" by this bind operation.
* @throws java.sql.SQLException Indicates problems performing the JDBC biind operation.
*/
int bind(PreparedStatement statement, QueryParameters qp, SharedSessionContractImplementor session, int position) throws SQLException;
public interface ParameterSpecification extends ParameterBinder {
/**
* Get the type which we are expeting for a bind into this parameter based
* on translated contextual information.

View File

@ -11,7 +11,7 @@ import java.sql.SQLException;
import org.hibernate.engine.spi.QueryParameters;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.type.Type;
import org.hibernate.engine.spi.TypedValue;
/**
* Parameter bind specification for an explicit positional (or ordinal) parameter.
@ -19,18 +19,24 @@ import org.hibernate.type.Type;
* @author Steve Ebersole
*/
public class PositionalParameterSpecification extends AbstractExplicitParameterSpecification {
private final int hqlPosition;
private final int label;
private final int bindingPosition;
/**
* Constructs a position/ordinal parameter bind specification.
*
* @param sourceLine See {@link #getSourceLine()}
* @param sourceColumn See {@link #getSourceColumn()}
* @param hqlPosition The position in the source query, relative to the other source positional parameters.
* @param label The position in the source query, relative to the other source positional parameters.
*/
public PositionalParameterSpecification(int sourceLine, int sourceColumn, int hqlPosition) {
public PositionalParameterSpecification(
int sourceLine,
int sourceColumn,
int label,
int bindingPosition) {
super( sourceLine, sourceColumn );
this.hqlPosition = hqlPosition;
this.label = label;
this.bindingPosition = bindingPosition;
}
/**
@ -45,24 +51,17 @@ public class PositionalParameterSpecification extends AbstractExplicitParameterS
*/
@Override
public int bind(PreparedStatement statement, QueryParameters qp, SharedSessionContractImplementor session, int position) throws SQLException {
Type type = qp.getPositionalParameterTypes()[hqlPosition];
Object value = qp.getPositionalParameterValues()[hqlPosition];
type.nullSafeSet( statement, value, position, session );
return type.getColumnSpan( session.getFactory() );
final TypedValue typedValue = qp.getNamedParameters().get( Integer.toString( label ) );
typedValue.getType().nullSafeSet( statement, typedValue.getValue(), position, session );
return typedValue.getType().getColumnSpan( session.getFactory() );
}
@Override
public String renderDisplayInfo() {
return "ordinal=" + hqlPosition + ", expectedType=" + getExpectedType();
return "label=" + label + ", expectedType=" + getExpectedType();
}
/**
* Getter for property 'hqlPosition'.
*
* @return Value for property 'hqlPosition'.
*/
public int getHqlPosition() {
return hqlPosition;
public int getLabel() {
return label;
}
}

View File

@ -46,7 +46,7 @@ public final class NamedQueryCollectionInitializer implements CollectionInitiali
);
}
else {
nativeQuery.setParameter( 0, key, persister.getKeyType() );
nativeQuery.setParameter( 1, key, persister.getKeyType() );
}
nativeQuery.setCollectionKey( key ).setFlushMode( FlushMode.MANUAL ).list();

View File

@ -29,6 +29,8 @@ public final class NamedQueryLoader implements UniqueEntityLoader {
private final String queryName;
private final EntityPersister persister;
private final int position;
/**
* Constructs the NamedQueryLoader
*
@ -39,6 +41,9 @@ public final class NamedQueryLoader implements UniqueEntityLoader {
super();
this.queryName = queryName;
this.persister = persister;
this.position = persister.getFactory().getSessionFactoryOptions().jdbcStyleParamsZeroBased()
? 0
: 1;
}
@Override
@ -61,7 +66,7 @@ public final class NamedQueryLoader implements UniqueEntityLoader {
query.setParameter( query.getNamedParameters()[0], id, persister.getIdentifierType() );
}
else {
query.setParameter( 0, id, persister.getIdentifierType() );
query.setParameter( position, id, persister.getIdentifierType() );
}
query.setOptionalId( id );

View File

@ -6,6 +6,7 @@
*/
package org.hibernate.query;
import java.util.Collection;
import java.util.Set;
import javax.persistence.Parameter;
@ -54,10 +55,9 @@ public interface ParameterMetadata {
<T> QueryParameter<T> resolve(Parameter<T> param);
default boolean isOrdinalParametersZeroBased() {
return true;
}
Collection<QueryParameter> getPositionalParameters();
default void setOrdinalParametersZeroBased(boolean isZeroBased) {
}
Collection<QueryParameter> getNamedParameters();
int getParameterCount();
}

View File

@ -23,16 +23,9 @@ public interface QueryParameter<T> extends javax.persistence.Parameter<T> {
*/
Type getType();
/**
* JPA has a different definition of positional parameters than what legacy Hibernate HQL had. In JPA,
* the parameter holders are labelled (named :/). At any rate the semantics are different and we often
* need to understand which we are dealing with (and applications might too).
*
* @return {@code true} if this is a JPA-style positional parameter; {@code false} would indicate
* we have either a named parameter ({@link #getName()} would return a non-{@code null} value) or a native
* Hibernate positional parameter.
*/
boolean isJpaPositionalParameter();
int[] getSourceLocations();
// todo : add a method indicating whether this parameter is valid for use in "parameter list binding"
// actually this already implemented in 6.0 code and I'm not going to mess with
// this in earlier versions
}

View File

@ -31,6 +31,7 @@ import org.hibernate.CacheMode;
import org.hibernate.FlushMode;
import org.hibernate.LockMode;
import org.hibernate.LockOptions;
import org.hibernate.Query;
import org.hibernate.ScrollMode;
import org.hibernate.ScrollableResults;
import org.hibernate.engine.spi.RowSelection;
@ -558,6 +559,14 @@ public class CriteriaQueryTypeQueryAdapter<X> implements QueryImplementor<X> {
return this;
}
@Override
public Query<X> setParameterList(int position, Collection values) {
ExplicitParameterInfo parameterInfo = locateParameterByPosition( position );
parameterInfo.validateDateBind();
jpqlQuery.setParameter( position, values );
return this;
}
@Override
public QueryImplementor<X> setParameterList(String name, Collection values, Type type) {
ExplicitParameterInfo parameterInfo = locateParameterByName( name );
@ -566,6 +575,14 @@ public class CriteriaQueryTypeQueryAdapter<X> implements QueryImplementor<X> {
return this;
}
@Override
public Query<X> setParameterList(int position, Collection values, Type type) {
ExplicitParameterInfo parameterInfo = locateParameterByPosition( position );
parameterInfo.validateDateBind();
jpqlQuery.setParameter( position, values, type );
return this;
}
@Override
public QueryImplementor<X> setParameterList(String name, Object[] values, Type type) {
ExplicitParameterInfo parameterInfo = locateParameterByName( name );
@ -574,6 +591,14 @@ public class CriteriaQueryTypeQueryAdapter<X> implements QueryImplementor<X> {
return this;
}
@Override
public Query<X> setParameterList(int position, Object[] values, Type type) {
ExplicitParameterInfo parameterInfo = locateParameterByPosition( position );
parameterInfo.validateDateBind();
jpqlQuery.setParameter( position, values, type );
return this;
}
@Override
public QueryImplementor<X> setParameterList(String name, Object[] values) {
ExplicitParameterInfo parameterInfo = locateParameterByName( name );
@ -582,6 +607,14 @@ public class CriteriaQueryTypeQueryAdapter<X> implements QueryImplementor<X> {
return this;
}
@Override
public Query<X> setParameterList(int position, Object[] values) {
ExplicitParameterInfo parameterInfo = locateParameterByPosition( position );
parameterInfo.validateDateBind();
jpqlQuery.setParameter( position, values );
return this;
}
@Override
public <P> QueryImplementor<X> setParameter(QueryParameter<P> parameter, P value, Type type) {
final ExplicitParameterInfo parameterInfo = resolveParameterInfo( parameter );

View File

@ -503,7 +503,7 @@ public abstract class AbstractProducedQuery<R> implements QueryImplementor<R> {
setParameter( position, typedParameterValue.getValue(), typedParameterValue.getType() );
}
else if ( value instanceof Collection && !isRegisteredAsBasicType( value.getClass() ) ) {
setParameterList( Integer.toString( position ), (Collection) value );
setParameterList( parameterMetadata.getQueryParameter( position ), (Collection) value );
}
else {
queryParameterBindings.getBinding( position ).setBindValue( value );
@ -567,6 +567,13 @@ public abstract class AbstractProducedQuery<R> implements QueryImplementor<R> {
return this;
}
@Override
@SuppressWarnings("unchecked")
public QueryImplementor setParameterList(int position, Collection values) {
queryParameterBindings.getQueryParameterListBinding( position ).setBindValues( values );
return this;
}
@Override
@SuppressWarnings("unchecked")
public QueryImplementor setParameterList(String name, Collection values, Type type) {
@ -574,6 +581,13 @@ public abstract class AbstractProducedQuery<R> implements QueryImplementor<R> {
return this;
}
@Override
@SuppressWarnings("unchecked")
public QueryImplementor setParameterList(int position, Collection values, Type type) {
queryParameterBindings.getQueryParameterListBinding( position ).setBindValues( values, type );
return this;
}
@Override
@SuppressWarnings("unchecked")
public QueryImplementor setParameterList(String name, Object[] values, Type type) {
@ -581,6 +595,13 @@ public abstract class AbstractProducedQuery<R> implements QueryImplementor<R> {
return this;
}
@Override
@SuppressWarnings("unchecked")
public QueryImplementor setParameterList(int position, Object[] values, Type type) {
queryParameterBindings.getQueryParameterListBinding( position ).setBindValues( Arrays.asList( values ), type );
return this;
}
@Override
@SuppressWarnings("unchecked")
public QueryImplementor setParameterList(String name, Object[] values) {
@ -588,6 +609,13 @@ public abstract class AbstractProducedQuery<R> implements QueryImplementor<R> {
return this;
}
@Override
@SuppressWarnings("unchecked")
public QueryImplementor setParameterList(int position, Object[] values) {
queryParameterBindings.getQueryParameterListBinding( position ).setBindValues( Arrays.asList( values ) );
return this;
}
@Override
@SuppressWarnings("unchecked")
public QueryImplementor setParameter(Parameter<Calendar> param, Calendar value, TemporalType temporalType) {
@ -1271,19 +1299,14 @@ public abstract class AbstractProducedQuery<R> implements QueryImplementor<R> {
// throw new IllegalArgumentException( "Could not unwrap this [" + toString() + "] as requested Java type [" + cls.getName() + "]" );
}
public QueryParameters getQueryParameters() {
protected QueryParameters makeQueryParametersForExecution(String hql) {
final HQLQueryPlan entityGraphHintedQueryPlan;
if ( entityGraphQueryHint == null) {
entityGraphHintedQueryPlan = null;
}
else {
queryParameterBindings.verifyParametersBound( false );
// todo : ideally we'd update the instance state related to queryString but that is final atm
final String expandedQuery = queryParameterBindings.expandListValuedParameters( getQueryString(), getProducer() );
entityGraphHintedQueryPlan = new HQLQueryPlan(
expandedQuery,
hql,
false,
getProducer().getLoadQueryInfluencers().getEnabledFilters(),
getProducer().getFactory(),
@ -1291,10 +1314,8 @@ public abstract class AbstractProducedQuery<R> implements QueryImplementor<R> {
);
}
QueryParameters queryParameters = new QueryParameters(
getPositionalParameterTypes(),
getPositionalParameterValues(),
getNamedParameterMap(),
QueryParameters queryParameters = new QueryParameters(
queryParameterBindings,
getLockOptions(),
queryOptions,
true,
@ -1316,6 +1337,11 @@ public abstract class AbstractProducedQuery<R> implements QueryImplementor<R> {
return queryParameters;
}
public QueryParameters getQueryParameters() {
final String expandedQuery = queryParameterBindings.expandListValuedParameters( getQueryString(), getProducer() );
return makeQueryParametersForExecution( expandedQuery );
}
@SuppressWarnings("deprecation")
protected Type[] getPositionalParameterTypes() {
return queryParameterBindings.collectPositionalBindTypes();
@ -1335,7 +1361,9 @@ public abstract class AbstractProducedQuery<R> implements QueryImplementor<R> {
private CacheMode sessionCacheMode;
protected void beforeQuery() {
queryParameterBindings.verifyParametersBound( isCallable() );
if ( optionalId == null ) {
queryParameterBindings.verifyParametersBound( isCallable() );
}
assert sessionFlushMode == null;
assert sessionCacheMode == null;
@ -1405,7 +1433,7 @@ public abstract class AbstractProducedQuery<R> implements QueryImplementor<R> {
return EmptyScrollableResults.INSTANCE;
}
final String query = queryParameterBindings.expandListValuedParameters( getQueryString(), getProducer() );
QueryParameters queryParameters = getQueryParameters();
QueryParameters queryParameters = makeQueryParametersForExecution( query );
queryParameters.setScrollMode( scrollMode );
return getProducer().scroll( query, queryParameters );
}
@ -1467,9 +1495,10 @@ public abstract class AbstractProducedQuery<R> implements QueryImplementor<R> {
}
}
final String expandedQuery = queryParameterBindings.expandListValuedParameters( getQueryString(), getProducer() );
return getProducer().list(
queryParameterBindings.expandListValuedParameters( getQueryString(), getProducer() ),
getQueryParameters()
expandedQuery,
makeQueryParametersForExecution( expandedQuery )
);
}
@ -1548,9 +1577,10 @@ public abstract class AbstractProducedQuery<R> implements QueryImplementor<R> {
}
protected int doExecuteUpdate() {
final String expandedQuery = queryParameterBindings.expandListValuedParameters( getQueryString(), getProducer() );
return getProducer().executeUpdate(
queryParameterBindings.expandListValuedParameters( getQueryString(), getProducer() ),
getQueryParameters()
expandedQuery,
makeQueryParametersForExecution( expandedQuery )
);
}

View File

@ -11,6 +11,7 @@ import java.util.List;
import org.hibernate.HibernateException;
import org.hibernate.ScrollMode;
import org.hibernate.engine.spi.QueryParameters;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.query.Query;
import org.hibernate.query.spi.ScrollableResultsImplementor;
@ -48,20 +49,24 @@ public class CollectionFilterImpl extends org.hibernate.query.internal.AbstractP
@Override
public Iterator iterate() throws HibernateException {
getQueryParameterBindings().verifyParametersBound( false );
final String expandedQuery = getQueryParameterBindings().expandListValuedParameters( getQueryString(), getProducer() );
return getProducer().iterateFilter(
collection,
getQueryParameterBindings().expandListValuedParameters( getQueryString(), getProducer() ),
getQueryParameters()
expandedQuery,
makeQueryParametersForExecution( expandedQuery )
);
}
@Override
public List list() throws HibernateException {
getQueryParameterBindings().verifyParametersBound( false );
final String expandedQuery = getQueryParameterBindings().expandListValuedParameters( getQueryString(), getProducer() );
return getProducer().listFilter(
collection,
getQueryParameterBindings().expandListValuedParameters( getQueryString(), getProducer() ),
getQueryParameters()
expandedQuery,
makeQueryParametersForExecution( expandedQuery )
);
}

View File

@ -126,10 +126,6 @@ public class NativeQueryImpl<T> extends AbstractProducedQuery<T> implements Nati
return this;
}
public void setZeroBasedParametersIndex(boolean zeroBasedParametersIndex) {
getParameterMetadata().setOrdinalParametersZeroBased( zeroBasedParametersIndex );
}
@Override
public String getQueryString() {
return sqlString;

View File

@ -6,17 +6,22 @@
*/
package org.hibernate.query.internal;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import javax.persistence.Parameter;
import org.hibernate.QueryException;
import org.hibernate.QueryParameterException;
import org.hibernate.engine.query.spi.NamedParameterDescriptor;
import org.hibernate.engine.query.spi.OrdinalParameterDescriptor;
import org.hibernate.internal.util.collections.ArrayHelper;
import org.hibernate.internal.util.StringHelper;
import org.hibernate.internal.util.compare.ComparableComparator;
import org.hibernate.query.ParameterMetadata;
import org.hibernate.query.QueryParameter;
import org.hibernate.type.Type;
@ -27,73 +32,62 @@ import org.hibernate.type.Type;
* @author Steve Ebersole
*/
public class ParameterMetadataImpl implements ParameterMetadata {
private static final OrdinalParameterDescriptor[] EMPTY_ORDINALS = new OrdinalParameterDescriptor[0];
private final OrdinalParameterDescriptor[] ordinalDescriptors;
private final Map<Integer,OrdinalParameterDescriptor> ordinalDescriptorMap;
private final Map<String,NamedParameterDescriptor> namedDescriptorMap;
private boolean isOrdinalParametersZeroBased = true;
private ParameterMetadataImpl(
OrdinalParameterDescriptor[] ordinalDescriptors,
Map<String, NamedParameterDescriptor> namedDescriptorMap, boolean isOrdinalParametersZeroBased) {
this.ordinalDescriptors = ordinalDescriptors;
this.namedDescriptorMap = namedDescriptorMap;
this.isOrdinalParametersZeroBased = isOrdinalParametersZeroBased;
}
/**
* Instantiates a ParameterMetadata container.
*
* @param ordinalDescriptors Descriptors of the ordinal parameters
* @param namedDescriptorMap Descriptors of the named parameters
*/
public ParameterMetadataImpl(
OrdinalParameterDescriptor[] ordinalDescriptors,
Map<String,NamedParameterDescriptor> namedDescriptorMap) {
if ( ordinalDescriptors == null ) {
this.ordinalDescriptors = EMPTY_ORDINALS;
}
else {
final OrdinalParameterDescriptor[] copy = new OrdinalParameterDescriptor[ ordinalDescriptors.length ];
System.arraycopy( ordinalDescriptors, 0, copy, 0, ordinalDescriptors.length );
this.ordinalDescriptors = copy;
}
Map<Integer,OrdinalParameterDescriptor> ordinalDescriptorMap,
Map<String, NamedParameterDescriptor> namedDescriptorMap) {
this.ordinalDescriptorMap = ordinalDescriptorMap == null
? Collections.emptyMap()
: Collections.unmodifiableMap( ordinalDescriptorMap );
this.namedDescriptorMap = namedDescriptorMap == null
? Collections.emptyMap()
: Collections.unmodifiableMap( namedDescriptorMap );
if (ordinalDescriptorMap != null && ! ordinalDescriptorMap.isEmpty() ) {
final List<Integer> sortedPositions = new ArrayList<>( ordinalDescriptorMap.keySet() );
sortedPositions.sort( ComparableComparator.INSTANCE );
int lastPosition = -1;
for ( Integer sortedPosition : sortedPositions ) {
if ( lastPosition == -1 ) {
lastPosition = sortedPosition;
continue;
}
if ( sortedPosition != lastPosition + 1 ) {
throw new QueryException(
String.format(
Locale.ROOT,
"Unexpected gap in ordinal parameter labels [%s -> %s] : [%s]",
lastPosition,
sortedPosition,
StringHelper.join( ",", sortedPositions )
)
);
}
lastPosition = sortedPosition;
}
if ( namedDescriptorMap == null ) {
this.namedDescriptorMap = java.util.Collections.emptyMap();
}
else {
final int size = (int) ( ( namedDescriptorMap.size() / .75 ) + 1 );
final Map<String,NamedParameterDescriptor> copy = new HashMap<>( size );
copy.putAll( namedDescriptorMap );
this.namedDescriptorMap = java.util.Collections.unmodifiableMap( copy );
}
}
@Override
@SuppressWarnings("unchecked")
public Set<QueryParameter<?>> collectAllParameters() {
if ( hasNamedParameters() || hasPositionalParameters() ) {
final HashSet allParameters = new HashSet();
allParameters.addAll( namedDescriptorMap.values() );
allParameters.addAll( ArrayHelper.toList( ordinalDescriptors ) );
return allParameters;
}
return Collections.emptySet();
public Collection<QueryParameter> getPositionalParameters() {
return Collections.unmodifiableCollection( ordinalDescriptorMap.values() );
}
@Override
@SuppressWarnings("unchecked")
public Set<Parameter<?>> collectAllParametersJpa() {
if ( hasNamedParameters() || hasPositionalParameters() ) {
final HashSet allParameters = new HashSet();
allParameters.addAll( namedDescriptorMap.values() );
allParameters.addAll( ArrayHelper.toList( ordinalDescriptors ) );
return allParameters;
}
public Collection<QueryParameter> getNamedParameters() {
return Collections.unmodifiableCollection( namedDescriptorMap.values() );
}
return Collections.emptySet();
@Override
public int getParameterCount() {
return ordinalDescriptorMap.size() + namedDescriptorMap.size();
}
@Override
@ -112,7 +106,16 @@ public class ParameterMetadataImpl implements ParameterMetadata {
}
public int getOrdinalParameterCount() {
return ordinalDescriptors.length;
return ordinalDescriptorMap.size();
}
@Override
public Set<String> getNamedParameterNames() {
return namedDescriptorMap.keySet();
}
public Set<Integer> getOrdinalParameterLabels() {
return ordinalDescriptorMap.keySet();
}
/**
@ -125,16 +128,18 @@ public class ParameterMetadataImpl implements ParameterMetadata {
* @throws QueryParameterException If the position is out of range
*/
public OrdinalParameterDescriptor getOrdinalParameterDescriptor(int position) {
if ( !isOrdinalParametersZeroBased ) {
position--;
}
if ( position < 0 || position >= ordinalDescriptors.length ) {
throw new QueryParameterException(
"Position beyond number of declared ordinal parameters. " +
"Remember that ordinal parameters are 0-based! Position: " + position
final OrdinalParameterDescriptor descriptor = ordinalDescriptorMap.get( position );
if ( descriptor == null ) {
throw new IllegalArgumentException(
String.format(
Locale.ROOT,
"Could not locate ordinal parameter [%s], expecting one of [%s]",
position,
StringHelper.join( ", ", ordinalDescriptorMap.keySet() )
)
);
}
return ordinalDescriptors[position];
return descriptor;
}
/**
@ -159,25 +164,17 @@ public class ParameterMetadataImpl implements ParameterMetadata {
*
* @return The source location
*
* @deprecated Use {@link OrdinalParameterDescriptor#getSourceLocation()} from the
* @deprecated Use {@link OrdinalParameterDescriptor#getPosition()} from the
* {@link #getOrdinalParameterDescriptor} return instead
*/
@Deprecated
public int getOrdinalParameterSourceLocation(int position) {
return getOrdinalParameterDescriptor( position ).getSourceLocation();
}
/**
* Access to the names of all named parameters
*
* @return The named parameter names
*/
public Set<String> getNamedParameterNames() {
return namedDescriptorMap.keySet();
return getOrdinalParameterDescriptor( position ).getPosition();
}
@Override
public <T> QueryParameter<T> getQueryParameter(String name) {
//noinspection unchecked
return getNamedParameterDescriptor( name );
}
@ -214,11 +211,18 @@ public class ParameterMetadataImpl implements ParameterMetadata {
* @throws QueryParameterException If the name could not be resolved to a named parameter
*/
public NamedParameterDescriptor getNamedParameterDescriptor(String name) {
final NamedParameterDescriptor meta = namedDescriptorMap.get( name );
if ( meta == null ) {
throw new QueryParameterException( "could not locate named parameter [" + name + "]" );
final NamedParameterDescriptor descriptor = namedDescriptorMap.get( name );
if ( descriptor == null ) {
throw new QueryParameterException(
String.format(
Locale.ROOT,
"Could not locate named parameter [%s], expecting one of [%s]",
name,
StringHelper.join( ", ", namedDescriptorMap.keySet() )
)
);
}
return meta;
return descriptor;
}
/**
@ -243,7 +247,7 @@ public class ParameterMetadataImpl implements ParameterMetadata {
*
* @return The type
*
* @deprecated Use {@link NamedParameterDescriptor#getSourceLocations()} from the
* @deprecated Use {@link NamedParameterDescriptor#getPosition()} from the
* {@link #getNamedParameterDescriptor} return instead
*/
@Deprecated
@ -252,20 +256,28 @@ public class ParameterMetadataImpl implements ParameterMetadata {
}
@Override
public boolean isOrdinalParametersZeroBased() {
return isOrdinalParametersZeroBased;
@SuppressWarnings("unchecked")
public Set<QueryParameter<?>> collectAllParameters() {
if ( hasNamedParameters() || hasPositionalParameters() ) {
final HashSet allParameters = new HashSet();
allParameters.addAll( namedDescriptorMap.values() );
allParameters.addAll( ordinalDescriptorMap.values() );
return allParameters;
}
return Collections.emptySet();
}
@Override
public void setOrdinalParametersZeroBased(boolean isZeroBased) {
this.isOrdinalParametersZeroBased = isZeroBased;
}
@SuppressWarnings("unchecked")
public Set<Parameter<?>> collectAllParametersJpa() {
if ( hasNamedParameters() || hasPositionalParameters() ) {
final HashSet allParameters = new HashSet();
allParameters.addAll( namedDescriptorMap.values() );
allParameters.addAll( ordinalDescriptorMap.values() );
return allParameters;
}
public ParameterMetadataImpl getOrdinalParametersZeroBasedCopy() {
return new ParameterMetadataImpl(
this.ordinalDescriptors,
this.namedDescriptorMap,
true
);
return Collections.emptySet();
}
}

View File

@ -7,14 +7,18 @@
package org.hibernate.query.internal;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.stream.Collectors;
import javax.persistence.Parameter;
import org.hibernate.HibernateException;
import org.hibernate.Incubating;
import org.hibernate.QueryException;
@ -22,13 +26,15 @@ import org.hibernate.QueryParameterException;
import org.hibernate.dialect.Dialect;
import org.hibernate.engine.jdbc.spi.JdbcServices;
import org.hibernate.engine.query.spi.NamedParameterDescriptor;
import org.hibernate.engine.query.spi.OrdinalParameterDescriptor;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.engine.spi.TypedValue;
import org.hibernate.hql.internal.classic.ParserHelper;
import org.hibernate.internal.CoreLogging;
import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.internal.util.StringHelper;
import org.hibernate.internal.util.collections.ArrayHelper;
import org.hibernate.internal.util.collections.CollectionHelper;
import org.hibernate.query.ParameterMetadata;
import org.hibernate.query.QueryParameter;
import org.hibernate.query.spi.QueryParameterBinding;
@ -51,73 +57,88 @@ public class QueryParameterBindingsImpl implements QueryParameterBindings {
private final ParameterMetadata parameterMetadata;
private final boolean queryParametersValidationEnabled;
private final int ordinalParamValueOffset;
private Map<QueryParameter, QueryParameterBinding> parameterBindingMap;
private Map<QueryParameter, QueryParameterListBinding> parameterListBindingMap;
private Map<Integer, QueryParameterBinding> positionalParameterBindings;
private Set<QueryParameter> parametersConvertedToListBindings;
public static QueryParameterBindingsImpl from(
ParameterMetadata parameterMetadata,
SessionFactoryImplementor sessionFactory,
boolean queryParametersValidationEnabled) {
if ( parameterMetadata == null ) {
return new QueryParameterBindingsImpl(
sessionFactory,
parameterMetadata,
queryParametersValidationEnabled
);
}
else {
return new QueryParameterBindingsImpl(
sessionFactory,
parameterMetadata.collectAllParameters(),
parameterMetadata, queryParametersValidationEnabled
);
throw new QueryParameterException( "Query parameter metadata cannot be null" );
}
return new QueryParameterBindingsImpl(
sessionFactory,
parameterMetadata,
queryParametersValidationEnabled
);
}
private QueryParameterBindingsImpl(
SessionFactoryImplementor sessionFactory,
ParameterMetadata parameterMetadata,
boolean queryParametersValidationEnabled) {
this( sessionFactory, Collections.emptySet(), parameterMetadata, queryParametersValidationEnabled );
}
private QueryParameterBindingsImpl(
SessionFactoryImplementor sessionFactory,
Set<QueryParameter<?>> queryParameters,
ParameterMetadata parameterMetadata,
boolean queryParametersValidationEnabled) {
this.sessionFactory = sessionFactory;
this.parameterMetadata = parameterMetadata;
this.queryParametersValidationEnabled = queryParametersValidationEnabled;
this.positionalParameterBindings = new TreeMap<>( );
if ( queryParameters == null || queryParameters.isEmpty() ) {
parameterBindingMap = Collections.emptyMap();
}
else {
parameterBindingMap = new HashMap<>();
this.parameterBindingMap = CollectionHelper.concurrentMap( parameterMetadata.getParameterCount() );
for ( QueryParameter queryParameter : queryParameters ) {
if ( parameterMetadata.hasPositionalParameters() ) {
int smallestOrdinalParamLabel = Integer.MAX_VALUE;
for ( QueryParameter queryParameter : parameterMetadata.getPositionalParameters() ) {
if ( queryParameter.getPosition() == null ) {
// only cache the non-positional parameters in this map
// positional parameters will be bound dynamically with getBinding(int)
parameterBindingMap.put( queryParameter, makeBinding( queryParameter ) );
throw new HibernateException( "Non-ordinal parameter ended up in ordinal param list" );
}
if ( queryParameter.getPosition() < smallestOrdinalParamLabel ) {
smallestOrdinalParamLabel = queryParameter.getPosition();
}
}
ordinalParamValueOffset = smallestOrdinalParamLabel;
}
else {
ordinalParamValueOffset = 0;
}
parameterListBindingMap = new HashMap<>();
}
@SuppressWarnings("WeakerAccess")
protected QueryParameterBinding makeBinding(QueryParameter queryParameter) {
return makeBinding( queryParameter.getType() );
}
@SuppressWarnings("WeakerAccess")
protected QueryParameterBinding makeBinding(Type bindType) {
return new QueryParameterBindingImpl( bindType, sessionFactory, shouldValidateBindingValue() );
}
@SuppressWarnings({"unchecked", "WeakerAccess"})
protected <T> QueryParameterListBinding<T> makeListBinding(QueryParameter<T> param) {
if ( parametersConvertedToListBindings == null ) {
parametersConvertedToListBindings = new HashSet<>();
}
parametersConvertedToListBindings.add( param );
if ( parameterListBindingMap == null ) {
parameterListBindingMap = new HashMap<>();
}
return parameterListBindingMap.computeIfAbsent(
param,
p -> new QueryParameterListBindingImpl(
param.getType(),
shouldValidateBindingValue()
)
);
}
@Override
@SuppressWarnings( "unchecked" )
public boolean isBound(QueryParameter parameter) {
final QueryParameterBinding binding = locateBinding( parameter );
if ( binding != null ) {
@ -171,16 +192,6 @@ public class QueryParameterBindingsImpl implements QueryParameterBindings {
return null;
}
protected QueryParameterBinding locateBinding(String name) {
for ( Map.Entry<QueryParameter, QueryParameterBinding> entry : parameterBindingMap.entrySet() ) {
if ( name.equals( entry.getKey().getName() ) ) {
return entry.getValue();
}
}
return null;
}
protected QueryParameterBinding locateAndRemoveBinding(String name) {
final Iterator<Map.Entry<QueryParameter, QueryParameterBinding>> entryIterator = parameterBindingMap.entrySet().iterator();
while ( entryIterator.hasNext() ) {
@ -194,117 +205,69 @@ public class QueryParameterBindingsImpl implements QueryParameterBindings {
return null;
}
protected QueryParameterBinding locateBinding(int position) {
if ( position < positionalParameterBindings.size() ) {
return positionalParameterBindings.get( position );
}
return null;
}
public QueryParameterBinding getBinding(String name) {
final QueryParameterBinding binding = locateBinding( name );
if ( binding == null ) {
throw new IllegalArgumentException( "Unknown parameter name : " + name );
}
return binding;
}
@Override
@SuppressWarnings("unchecked")
public QueryParameterBinding getBinding(int position) {
int positionAdjustment = 0;
if ( !parameterMetadata.isOrdinalParametersZeroBased() ) {
positionAdjustment = -1;
}
QueryParameterBinding binding = null;
if ( parameterMetadata != null ) {
if ( !parameterMetadata.hasPositionalParameters() ) {
// no positional parameters, assume jpa named.
binding = locateBinding( Integer.toString( position ) );
}
else {
try {
binding = positionalParameterBindings.get( position + positionAdjustment );
if ( binding == null ) {
binding = makeBinding( parameterMetadata.getQueryParameter( position ) );
positionalParameterBindings.put( position + positionAdjustment, binding );
}
}
catch (QueryParameterException e) {
// treat this as null binding
}
}
return locateBinding( position );
}
@SuppressWarnings("WeakerAccess")
protected QueryParameterBinding locateBinding(int position) {
final QueryParameter<Object> param = parameterMetadata.getQueryParameter( position );
if ( param == null ) {
throw new IllegalArgumentException( "Unknown ordinal parameter : " + position );
}
if ( binding == null ) {
throw new IllegalArgumentException( "Unknown parameter position: " + position );
return parameterBindingMap.computeIfAbsent(
param,
this::makeBinding
);
}
@Override
@SuppressWarnings("unchecked")
public QueryParameterBinding getBinding(String name) {
return locateBinding( name );
}
@SuppressWarnings("WeakerAccess")
protected QueryParameterBinding locateBinding(String name) {
final QueryParameter<Object> param = parameterMetadata.getQueryParameter( name );
if ( param == null ) {
throw new IllegalArgumentException( "Unknown named parameter : " + name );
}
return binding;
return parameterBindingMap.computeIfAbsent(
param,
this::makeBinding
);
}
public void verifyParametersBound(boolean reserveFirstParameter) {
// verify named parameters bound
for ( Map.Entry<QueryParameter, QueryParameterBinding> bindEntry : parameterBindingMap.entrySet() ) {
if ( !bindEntry.getValue().isBound() ) {
if ( bindEntry.getKey().getName() != null ) {
throw new QueryException( "Named parameter [" + bindEntry.getKey().getName() + "] not set" );
}
else {
throw new QueryException( "Parameter memento [" + bindEntry.getKey() + "] not set" );
}
for ( QueryParameter<?> parameter : parameterMetadata.collectAllParameters() ) {
// check the "normal" bindings
if ( parameterBindingMap.containsKey( parameter ) ) {
continue;
}
}
// verify position parameters bound
int startIndex = 0;
if ( !parameterMetadata.isOrdinalParametersZeroBased() ) {
startIndex = 1;
}
for ( int i = startIndex; i < positionalParameterBindings.size(); i++ ) {
QueryParameterBinding binding = null;
if ( parameterMetadata.isOrdinalParametersZeroBased() ) {
binding = positionalParameterBindings.get( i );
// next check the "list" bindings
if ( parameterListBindingMap != null
&& parameterListBindingMap.containsKey( parameter ) ) {
continue;
}
if ( parametersConvertedToListBindings != null
&& parametersConvertedToListBindings.contains( parameter ) ) {
continue;
}
if ( parameter.getName() != null ) {
throw new QueryException( "Named parameter not bound : " + parameter.getName() );
}
else {
binding = positionalParameterBindings.get( i - 1 );
}
if ( binding == null || !binding.isBound() ) {
throw new QueryException( "Positional parameter [" + i + "] not set" );
throw new QueryException( "Ordinal parameter not bound : " + parameter.getPosition() );
}
}
// verify position parameter count is correct
final int positionalValueSpan = calculatePositionalValueSpan( reserveFirstParameter );
final int positionCounts = parameterMetadata.getPositionalParameterCount();
if ( positionCounts != positionalValueSpan ) {
if ( reserveFirstParameter && positionCounts - 1 != positionalValueSpan ) {
throw new QueryException(
"Expected positional parameter count: " +
( positionCounts - 1 ) +
", actually detected " + positionalValueSpan
);
}
else if ( !reserveFirstParameter ) {
throw new QueryException(
"Expected positional parameter count: " +
( positionCounts ) +
", actually detected " + positionalValueSpan
);
}
}
}
private int calculatePositionalValueSpan(boolean reserveFirstParameter) {
int positionalValueSpan = 0;
for ( QueryParameterBinding binding : positionalParameterBindings.values() ) {
if ( binding.isBound() ) {
Type bindType = binding.getBindType();
if ( bindType == null ) {
bindType = SerializableType.INSTANCE;
}
positionalValueSpan += bindType.getColumnSpan( sessionFactory );
}
}
return positionalValueSpan;
}
/**
@ -334,42 +297,79 @@ public class QueryParameterBindingsImpl implements QueryParameterBindings {
*/
@Deprecated
public Type[] collectPositionalBindTypes() {
Type[] types = new Type[ positionalParameterBindings.size() ];
return ArrayHelper.EMPTY_TYPE_ARRAY;
// if ( ! parameterMetadata.hasPositionalParameters() ) {
// return ArrayHelper.EMPTY_TYPE_ARRAY;
// }
//
// // callers expect these in ordinal order. In a way that is natural, but at the same
// // time long term a way to find types/values by name/position would be better
//
// final TreeMap<QueryParameter, QueryParameterBinding> sortedPositionalParamBindings = getSortedPositionalParamBindingMap();
// final List<Type> types = CollectionHelper.arrayList( sortedPositionalParamBindings.size() );
//
// for ( Map.Entry<QueryParameter, QueryParameterBinding> entry : sortedPositionalParamBindings.entrySet() ) {
// if ( entry.getKey().getPosition() == null ) {
// continue;
// }
//
// Type type = entry.getValue().getBindType();
// if ( type == null ) {
// type = entry.getKey().getType();
// }
//
// if ( type == null ) {
// log.debugf(
// "Binding for positional-parameter [%s] did not define type, using SerializableType",
// entry.getKey().getPosition()
// );
// type = SerializableType.INSTANCE;
// }
//
// types.add( type );
// }
//
// return types.toArray( new Type[ types.size() ] );
}
// NOTE : bindings should be ordered by position by nature of a TreeMap...
// NOTE : we also assume the contiguity of the positions
private TreeMap<QueryParameter, QueryParameterBinding> getSortedPositionalParamBindingMap() {
final TreeMap<QueryParameter, QueryParameterBinding> map = new TreeMap<>( Comparator.comparing( Parameter::getPosition ) );
for ( Map.Entry<Integer, QueryParameterBinding> entry : positionalParameterBindings.entrySet() ) {
final int position = entry.getKey();
Type type = entry.getValue().getBindType();
if ( type == null ) {
log.debugf( "Binding for positional-parameter [%s] did not define type, using SerializableType", position );
type = SerializableType.INSTANCE;
for ( Map.Entry<QueryParameter, QueryParameterBinding> entry : parameterBindingMap.entrySet() ) {
if ( entry.getKey().getPosition() == null ) {
continue;
}
types[ position ] = type;
map.put( entry.getKey(), entry.getValue() );
}
return types;
return map;
}
private static final Object[] EMPTY_VALUES = new Object[0];
/**
* @deprecated (since 5.2) expect a different approach to org.hibernate.engine.spi.QueryParameters in 6.0
*/
@Deprecated
@SuppressWarnings("unchecked")
public Object[] collectPositionalBindValues() {
Object[] values = new Object[ positionalParameterBindings.size() ];
// NOTE : bindings should be ordered by position by nature of a TreeMap...
// NOTE : we also assume the contiguity of the positions
for ( Map.Entry<Integer, QueryParameterBinding> entry : positionalParameterBindings.entrySet() ) {
final int position = entry.getKey();
values[ position ] = entry.getValue().getBindValue();
}
return values;
return EMPTY_VALUES;
// if ( ! parameterMetadata.hasPositionalParameters() ) {
// return EMPTY_VALUES;
// }
//
// final TreeMap<QueryParameter, QueryParameterBinding> sortedPositionalParamBindings = getSortedPositionalParamBindingMap();
// final List values = CollectionHelper.arrayList( sortedPositionalParamBindings.size() );
//
// for ( Map.Entry<QueryParameter, QueryParameterBinding> entry : sortedPositionalParamBindings.entrySet() ) {
// if ( entry.getKey().getPosition() == null ) {
// continue;
// }
// values.add( entry.getValue().getBindValue() );
// }
//
// return values.toArray( new Object[values.size()] );
}
/**
@ -377,20 +377,25 @@ public class QueryParameterBindingsImpl implements QueryParameterBindings {
*/
@Deprecated
public Map<String, TypedValue> collectNamedParameterBindings() {
Map<String, TypedValue> collectedBindings = new HashMap<>();
final Map<String, TypedValue> collectedBindings = new HashMap<>();
for ( Map.Entry<QueryParameter, QueryParameterBinding> entry : parameterBindingMap.entrySet() ) {
if ( entry.getKey().getName() == null ) {
continue;
final String key;
if ( entry.getKey().getPosition() != null ) {
key = Integer.toString( entry.getKey().getPosition() );
}
else {
key = entry.getKey().getName();
}
Type bindType = entry.getValue().getBindType();
if ( bindType == null ) {
log.debugf( "Binding for named-parameter [%s] did not define type", entry.getKey().getName() );
log.debugf( "Binding for parameter [%s] did not define type", key );
bindType = SerializableType.INSTANCE;
}
collectedBindings.put(
entry.getKey().getName(),
key,
new TypedValue( bindType, entry.getValue().getBindValue() )
);
}
@ -408,11 +413,14 @@ public class QueryParameterBindingsImpl implements QueryParameterBindings {
@Deprecated
@SuppressWarnings("unchecked")
public <T> QueryParameterListBinding<T> getQueryParameterListBinding(QueryParameter<T> queryParameter) {
QueryParameterListBinding result = parameterListBindingMap.get( queryParameter );
if ( result == null ) {
result = transformQueryParameterBindingToQueryParameterListBinding( queryParameter );
if ( parameterListBindingMap == null ) {
parameterListBindingMap = new HashMap<>();
}
return result;
return parameterListBindingMap.computeIfAbsent(
queryParameter,
this::transformQueryParameterBindingToQueryParameterListBinding
);
}
/**
@ -420,41 +428,45 @@ public class QueryParameterBindingsImpl implements QueryParameterBindings {
*/
@Deprecated
private QueryParameterListBinding locateQueryParameterListBinding(QueryParameter queryParameter) {
QueryParameterListBinding result = parameterListBindingMap.get( queryParameter );
if ( parameterListBindingMap == null ) {
parameterListBindingMap = new HashMap<>();
}
if ( result == null && queryParameter.getName() != null ) {
for ( Map.Entry<QueryParameter, QueryParameterListBinding> entry : parameterListBindingMap.entrySet() ) {
if ( queryParameter.getName().equals( entry.getKey().getName() ) ) {
result = entry.getValue();
break;
}
QueryParameterListBinding binding = parameterListBindingMap.get( queryParameter );
if ( binding == null ) {
QueryParameter resolved = resolveParameter( queryParameter );
if ( resolved != queryParameter ) {
binding = parameterListBindingMap.get( resolved );
}
}
return result;
if ( binding == null ) {
throw new IllegalArgumentException( "Could not locate parameter list binding" );
}
return binding;
}
private QueryParameter resolveParameter(QueryParameter queryParameter) {
if ( queryParameter.getName() != null ) {
return parameterMetadata.getQueryParameter( queryParameter.getName() );
}
else {
return parameterMetadata.getQueryParameter( queryParameter.getPosition() );
}
}
/**
* @deprecated (since 5.2) expected changes to "collection-valued parameter binding" in 6.0
*/
@Deprecated
@SuppressWarnings("unchecked")
private <T> QueryParameterListBinding<T> transformQueryParameterBindingToQueryParameterListBinding(QueryParameter<T> queryParameter) {
log.debugf( "Converting QueryParameterBinding to QueryParameterListBinding for given QueryParameter : %s", queryParameter );
final QueryParameterBinding binding = getAndRemoveBinding( queryParameter );
if ( binding == null ) {
throw new IllegalArgumentException(
"Could not locate QueryParameterBinding for given QueryParameter : " + queryParameter +
"; parameter list must be defined using named parameter"
);
}
final QueryParameterListBinding<T> convertedBinding = new QueryParameterListBindingImpl<>(
binding.getBindType(),
shouldValidateBindingValue()
);
parameterListBindingMap.put( queryParameter, convertedBinding );
getAndRemoveBinding( queryParameter );
return convertedBinding;
return makeListBinding( queryParameter );
}
private boolean shouldValidateBindingValue() {
@ -467,25 +479,23 @@ public class QueryParameterBindingsImpl implements QueryParameterBindings {
@Deprecated
@SuppressWarnings("unchecked")
private <T> QueryParameterBinding<T> getAndRemoveBinding(QueryParameter<T> parameter) {
// see if this exact instance is known as a key
if ( parameterBindingMap.containsKey( parameter ) ) {
return parameterBindingMap.remove( parameter );
}
QueryParameterBinding<T> binding = parameterBindingMap.remove( parameter );
// if the incoming parameter has a name, try to find it by name
if ( StringHelper.isNotEmpty( parameter.getName() ) ) {
final QueryParameterBinding binding = locateAndRemoveBinding( parameter.getName() );
if ( binding != null ) {
return binding;
if ( binding == null ) {
if ( parameter.getName() != null ) {
parameter = parameterMetadata.getQueryParameter( parameter.getName() );
}
else {
parameter = parameterMetadata.getQueryParameter( parameter.getPosition() );
}
if ( parameter == null ) {
throw new HibernateException( "Unable to resolve QueryParameter" );
}
}
binding = parameterBindingMap.remove( parameter );
// NOTE : getAndRemoveBinding is only intended for usage from #transformQueryParameterBindingToQueryParameterListBinding
// which only supports named parameters, so there is no need to look into legacy positional parameters
throw new IllegalArgumentException(
"Could not resolve QueryParameter reference [" + parameter + "] to QueryParameterBinding"
);
return binding;
}
/**
@ -505,21 +515,43 @@ public class QueryParameterBindingsImpl implements QueryParameterBindings {
@Deprecated
@SuppressWarnings("unchecked")
private <T> QueryParameter<T> resolveQueryParameter(String name) {
for ( QueryParameter queryParameter : parameterListBindingMap.keySet() ) {
if ( name.equals( queryParameter.getName() ) ) {
return queryParameter;
}
final QueryParameter<Object> param = parameterMetadata.getQueryParameter( name );
if ( param == null ) {
throw new IllegalArgumentException(
"Unable to resolve given parameter name [" + name + "] to QueryParameter reference"
);
}
for ( QueryParameter queryParameter : parameterBindingMap.keySet() ) {
if ( name.equals( queryParameter.getName() ) ) {
return queryParameter;
}
return (QueryParameter<T>) param;
}
/**
* @deprecated (since 5.2) expected changes to "collection-valued parameter binding" in 6.0
*/
@Deprecated
@SuppressWarnings("unchecked")
public <T> QueryParameterListBinding<T> getQueryParameterListBinding(int name) {
// find the QueryParameter instance for the given name
final QueryParameter<T> queryParameter = resolveQueryParameter( name );
return getQueryParameterListBinding( queryParameter );
}
/**
* @deprecated (since 5.2) expected changes to "collection-valued parameter binding" in 6.0
*/
@Deprecated
@SuppressWarnings("unchecked")
private <T> QueryParameter<T> resolveQueryParameter(int name) {
final QueryParameter<Object> param = parameterMetadata.getQueryParameter( name );
if ( param == null ) {
throw new IllegalArgumentException(
"Unable to resolve given parameter name [" + name + "] to QueryParameter reference"
);
}
throw new IllegalArgumentException(
"Unable to resolve given parameter name [" + name + "] to QueryParameter reference"
);
return (QueryParameter<T>) param;
}
/**
@ -532,6 +564,10 @@ public class QueryParameterBindingsImpl implements QueryParameterBindings {
return null;
}
if ( parameterListBindingMap == null || parameterListBindingMap.isEmpty() ) {
return queryString;
}
// more-or-less... for each entry in parameterListBindingMap we will create an
// entry in parameterBindingMap for each of the values in the bound value list. afterwards
// we will clear the parameterListBindingMap.
@ -545,24 +581,29 @@ public class QueryParameterBindingsImpl implements QueryParameterBindings {
final int inExprLimit = dialect.getInExpressionCountLimit();
for ( Map.Entry<QueryParameter, QueryParameterListBinding> entry : parameterListBindingMap.entrySet() ) {
final NamedParameterDescriptor sourceParam = (NamedParameterDescriptor) entry.getKey();
final QueryParameter sourceParam = entry.getKey();
final Collection bindValues = entry.getValue().getBindValues();
if ( inExprLimit > 0 && bindValues.size() > inExprLimit ) {
log.tooManyInExpressions( dialect.getClass().getName(), inExprLimit, sourceParam.getName(), bindValues.size() );
}
final boolean isJpaPositionalParam = sourceParam.isJpaPositionalParameter();
final String paramPrefix = isJpaPositionalParam ? "?" : ParserHelper.HQL_VARIABLE_PREFIX;
final String placeholder = paramPrefix + sourceParam.getName();
final int loc = queryString.indexOf( placeholder );
final String sourceToken;
if ( sourceParam instanceof NamedParameterDescriptor ) {
sourceToken = ":" + NamedParameterDescriptor.class.cast( sourceParam ).getName();
}
else {
sourceToken = "?" + OrdinalParameterDescriptor.class.cast( sourceParam ).getPosition();
}
final int loc = queryString.indexOf( sourceToken );
if ( loc < 0 ) {
continue;
}
final String beforePlaceholder = queryString.substring( 0, loc );
final String afterPlaceholder = queryString.substring( loc + placeholder.length() );
final String afterPlaceholder = queryString.substring( loc + sourceToken.length() );
// check if placeholder is already immediately enclosed in parentheses
// (ignoring whitespace)
@ -583,21 +624,30 @@ public class QueryParameterBindingsImpl implements QueryParameterBindings {
int i = 0;
for ( Object bindValue : entry.getValue().getBindValues() ) {
if ( i > 0 ) {
expansionList.append( ", " );
}
// for each value in the bound list-of-values we:
// 1) create a synthetic named parameter
// 2) expand the queryString to include each synthetic named param in place of the original
// 3) create a new synthetic binding for just that single value under the synthetic name
final String syntheticName = ( isJpaPositionalParam ? 'x' : "" ) + sourceParam.getName() + '_' + i;
if ( i > 0 ) {
expansionList.append( ", " );
final String syntheticName;
if ( sourceParam instanceof NamedParameterDescriptor ) {
syntheticName = NamedParameterDescriptor.class.cast( sourceParam ).getName() + '_' + i;
}
expansionList.append( ParserHelper.HQL_VARIABLE_PREFIX ).append( syntheticName );
final QueryParameter syntheticParam = new QueryParameterNamedImpl<>(
else {
syntheticName = "x" + OrdinalParameterDescriptor.class.cast( sourceParam ).getPosition() + '_' + i;
}
expansionList.append( ":" ).append( syntheticName );
final QueryParameter syntheticParam = new NamedParameterDescriptor(
syntheticName,
sourceParam.getSourceLocations(),
sourceParam.isJpaPositionalParameter(),
sourceParam.getType()
sourceParam.getType(),
sourceParam.getSourceLocations()
);
final QueryParameterBinding syntheticBinding = makeBinding( entry.getValue().getBindType() );
syntheticBinding.setBindValue( bindValue );
parameterBindingMap.put( syntheticParam, syntheticBinding );
@ -607,13 +657,17 @@ public class QueryParameterBindingsImpl implements QueryParameterBindings {
queryString = StringHelper.replace(
beforePlaceholder,
afterPlaceholder,
placeholder,
sourceToken,
expansionList.toString(),
true,
true
);
}
if ( parameterListBindingMap != null ) {
parameterListBindingMap.clear();
}
return queryString;
}
}

View File

@ -22,13 +22,11 @@ import org.hibernate.type.Type;
public class QueryParameterNamedImpl<T> extends QueryParameterImpl<T> implements QueryParameter<T> {
private final String name;
private final int[] sourceLocations;
private final boolean jpaStyle;
public QueryParameterNamedImpl(String name, int[] sourceLocations, boolean jpaStyle, Type expectedType) {
public QueryParameterNamedImpl(String name, int[] sourceLocations, Type expectedType) {
super( expectedType );
this.name = name;
this.sourceLocations = sourceLocations;
this.jpaStyle = jpaStyle;
}
@Override
@ -45,11 +43,6 @@ public class QueryParameterNamedImpl<T> extends QueryParameterImpl<T> implements
return sourceLocations;
}
@Override
public boolean isJpaPositionalParameter() {
return jpaStyle;
}
@Override
public boolean equals(Object o) {
if ( this == o ) {

View File

@ -39,8 +39,8 @@ public class ProcedureParameterImpl<T> extends QueryParameterImpl<T> implements
}
@Override
public boolean isJpaPositionalParameter() {
return false;
public int[] getSourceLocations() {
return new int[0];
}
@Override

View File

@ -7,10 +7,12 @@
package org.hibernate.query.procedure.internal;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import javax.persistence.Parameter;
import org.hibernate.QueryParameterException;
@ -153,4 +155,19 @@ public class ProcedureParameterMetadata implements ParameterMetadata {
return null;
}
@Override
public Collection<QueryParameter> getPositionalParameters() {
return parameters.stream().filter( p -> p.getPosition() != null ).collect( Collectors.toList() );
}
@Override
public Collection<QueryParameter> getNamedParameters() {
return parameters.stream().filter( p -> p.getPosition() == null ).collect( Collectors.toList() );
}
@Override
public int getParameterCount() {
return parameters.size();
}
}

View File

@ -11,7 +11,6 @@ import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Supplier;
@ -22,6 +21,7 @@ import org.hibernate.internal.CoreLogging;
import org.hibernate.loader.custom.CustomLoader;
import org.hibernate.loader.custom.CustomQuery;
import org.hibernate.loader.custom.sql.SQLQueryReturnProcessor;
import org.hibernate.param.ParameterBinder;
import org.hibernate.result.NoMoreReturnsException;
import org.hibernate.result.Output;
import org.hibernate.result.Outputs;
@ -245,9 +245,9 @@ public class OutputsImpl implements Outputs {
}
@Override
public Map getNamedParameterBindPoints() {
// no named parameters in terms of embedded in the SQL string
return null;
public List<ParameterBinder> getParameterValueBinders() {
// no parameters in terms of embedded in the SQL string
return Collections.emptyList();
}
@Override

View File

@ -33,7 +33,7 @@ public class ParameterParserTest {
}
@Test
public void testQuotedTextInComment() {
ParamLocationRecognizer recognizer = new ParamLocationRecognizer();
ParamLocationRecognizer recognizer = new ParamLocationRecognizer( 0 );
ParameterParser.parse("-- 'This' should not fail the test.\n"
+ "SELECT column FROM Table WHERE column <> :param", recognizer);
@ -43,35 +43,35 @@ public class ParameterParserTest {
@Test
public void testContractionInComment() {
ParamLocationRecognizer recognizer = new ParamLocationRecognizer();
ParamLocationRecognizer recognizer = new ParamLocationRecognizer( 0 );
ParameterParser.parse("-- This shouldn't fail the test.\n" + "SELECT column FROM Table WHERE column <> :param",
recognizer);
assertTrue(recognizer.getNamedParameterDescriptionMap().containsKey("param"));
assertTrue( recognizer.getNamedParameterDescriptionMap().containsKey("param"));
}
@Test
public void testDoubleDashInCharLiteral() {
ParamLocationRecognizer recognizer = new ParamLocationRecognizer();
ParamLocationRecognizer recognizer = new ParamLocationRecognizer( 0 );
ParameterParser.parse("select coalesce(i.name, '--NONE--') as itname from Item i where i.intVal=? ",recognizer);
assertEquals( 1, recognizer.getOrdinalParameterLocationList().size() );
assertEquals( 1, recognizer.getOrdinalParameterDescriptionMap().size() );
}
@Test
public void testSlashStarInCharLiteral() {
ParamLocationRecognizer recognizer = new ParamLocationRecognizer();
ParamLocationRecognizer recognizer = new ParamLocationRecognizer( 0 );
ParameterParser.parse("select coalesce(i.name, '/*NONE') as itname from Item i where i.intVal=? ",recognizer);
assertEquals( 1, recognizer.getOrdinalParameterLocationList().size() );
assertEquals( 1, recognizer.getOrdinalParameterDescriptionMap().size() );
}
@Test
public void testApostropheInOracleAlias() {
ParamLocationRecognizer recognizer = new ParamLocationRecognizer();
ParamLocationRecognizer recognizer = new ParamLocationRecognizer( 0 );
ParameterParser.parse("SELECT column as \"Table's column\" FROM Table WHERE column <> :param", recognizer);
@ -87,30 +87,38 @@ public class ParameterParserTest {
public void outParameter(int position) {
fail();
}
@Override
public void ordinalParameter(int position) {
fail();
}
@Override
public void namedParameter(String name, int position) {
fail();
}
@Override
public void jpaPositionalParameter(String name, int position) {
public void jpaPositionalParameter(int name, int position) {
fail();
}
@Override
public void other(char character) {
captured.append(character);
}
};
@Override
public void complete() {
}
};
ParameterParser.parse("SELECT @a,(@a::=20) FROM tbl_name", recognizer);
assertEquals("SELECT @a,(@a:=20) FROM tbl_name", captured.toString());
}
@Test
public void testParseNamedParameter() {
ParamLocationRecognizer recognizer = new ParamLocationRecognizer();
ParamLocationRecognizer recognizer = new ParamLocationRecognizer( 0 );
ParameterParser.parse("from Stock s where s.stockCode = :stockCode and s.xyz = :pxyz", recognizer);
assertTrue(recognizer.getNamedParameterDescriptionMap().containsKey("stockCode"));
assertTrue(recognizer.getNamedParameterDescriptionMap().containsKey("pxyz"));

View File

@ -79,8 +79,8 @@ public class NamedQueryTest extends BaseEntityManagerFunctionalTestCase {
assertEquals( 1, list.size() );
final Session session = entityManager.unwrap( Session.class );
final org.hibernate.query.Query sessionQuery = session.createQuery( "select g from Game g where title = ?" );
sessionQuery.setParameter( 0, GAME_TITLES[0] );
final org.hibernate.query.Query sessionQuery = session.createQuery( "select g from Game g where title = ?1" );
sessionQuery.setParameter( 1, GAME_TITLES[0] );
list = sessionQuery.getResultList();
query.setParameter( 1, GAME_TITLES[0] );
@ -99,7 +99,7 @@ public class NamedQueryTest extends BaseEntityManagerFunctionalTestCase {
final Session session = entityManager.unwrap( Session.class );
final org.hibernate.query.Query sessionQuery = session.getNamedQuery( "NamedQuery" );
sessionQuery.setParameter( 0, GAME_TITLES[0] );
sessionQuery.setParameter( 1, GAME_TITLES[0] );
list = sessionQuery.getResultList();
query.setParameter( 1, GAME_TITLES[0] );
@ -130,7 +130,7 @@ public class NamedQueryTest extends BaseEntityManagerFunctionalTestCase {
final Session session = entityManager.unwrap( Session.class );
final org.hibernate.query.Query sessionQuery = session.createSQLQuery(
"select * from Game g where title = ?" );
sessionQuery.setParameter( 0, GAME_TITLES[0] );
sessionQuery.setParameter( 1, GAME_TITLES[0] );
list = sessionQuery.getResultList();
query.setParameter( 1, GAME_TITLES[0] );
@ -150,7 +150,7 @@ public class NamedQueryTest extends BaseEntityManagerFunctionalTestCase {
final Session session = entityManager.unwrap( Session.class );
final org.hibernate.query.Query sessionQuery = session.getNamedNativeQuery(
"NamedNativeQuery" );
sessionQuery.setParameter( 0, GAME_TITLES[0] );
sessionQuery.setParameter( 1, GAME_TITLES[0] );
list = sessionQuery.getResultList();
query.setParameter( 1, GAME_TITLES[0] );
@ -160,7 +160,7 @@ public class NamedQueryTest extends BaseEntityManagerFunctionalTestCase {
}
@Entity(name = "Game")
@NamedQueries(@NamedQuery(name = "NamedQuery", query = "select g from Game g where title = ?"))
@NamedQueries(@NamedQuery(name = "NamedQuery", query = "select g from Game g where title = ?1"))
@NamedNativeQueries(@NamedNativeQuery(name = "NamedNativeQuery", query = "select * from Game g where title = ?"))
public static class Game {
private Long id;

View File

@ -115,7 +115,7 @@ public class NativeQueryOrdinalParametersTest extends BaseEntityManagerFunctiona
final String sqlString = "SELECT * FROM GAME g WHERE title = ?";
try {
NativeQuery sqlQuery = em.unwrap( Session.class ).createSQLQuery( sqlString );
sqlQuery.setString( 0, "Super Mario Brothers").setCacheable( true );
sqlQuery.setString( 1, "Super Mario Brothers").setCacheable( true );
List results = sqlQuery.list();
assertEquals( 1, results.size() );
@ -126,7 +126,7 @@ public class NativeQueryOrdinalParametersTest extends BaseEntityManagerFunctiona
assertEquals( 1, list.size() );
sqlQuery = em.unwrap( Session.class ).createSQLQuery( sqlString );
sqlQuery.setString( 0, "Super Mario Brothers").setCacheable( true );
sqlQuery.setString( 1, "Super Mario Brothers").setCacheable( true );
results = sqlQuery.list();
assertEquals( 1, results.size() );

View File

@ -23,6 +23,7 @@ import javax.persistence.TemporalType;
import javax.persistence.Tuple;
import org.hibernate.Hibernate;
import org.hibernate.QueryException;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.dialect.Oracle8iDialect;
import org.hibernate.dialect.PostgreSQL9Dialect;
@ -139,17 +140,17 @@ public class QueryTest extends BaseEntityManagerFunctionalTestCase {
try {
Item item = new Item( "Mouse", "Micro$oft mouse" );
em.persist( item );
Query q = em.createQuery( "from Item i where i.intVal=?" );
q.setParameter( 0, null );
Query q = em.createQuery( "from Item i where i.intVal=?1" );
q.setParameter( 1, null );
List results = q.getResultList();
// null != null
assertEquals( 0, results.size() );
q = em.createQuery( "from Item i where i.intVal is null and ? is null" );
q.setParameter( 0, null );
q = em.createQuery( "from Item i where i.intVal is null and ?1 is null" );
q.setParameter( 1, null );
results = q.getResultList();
assertEquals( 1, results.size() );
q = em.createQuery( "from Item i where i.intVal is null or i.intVal = ?" );
q.setParameter( 0, null );
q = em.createQuery( "from Item i where i.intVal is null or i.intVal = ?1" );
q.setParameter( 1, null );
results = q.getResultList();
assertEquals( 1, results.size() );
}
@ -169,7 +170,7 @@ public class QueryTest extends BaseEntityManagerFunctionalTestCase {
try {
Item item = new Item( "Mouse", "Micro$oft mouse" );
em.persist( item );
Query q = em.createQuery( "from Item i where i.intVal=?" );
Query q = em.createQuery( "from Item i where i.intVal=?1" );
Parameter p = new Parameter() {
@Override
public String getName() {
@ -178,7 +179,7 @@ public class QueryTest extends BaseEntityManagerFunctionalTestCase {
@Override
public Integer getPosition() {
return 0;
return 1;
}
@Override
@ -191,11 +192,11 @@ public class QueryTest extends BaseEntityManagerFunctionalTestCase {
List results = q.getResultList();
// null != null
assertEquals( 0, results.size() );
q = em.createQuery( "from Item i where i.intVal is null and ? is null" );
q = em.createQuery( "from Item i where i.intVal is null and ?1 is null" );
q.setParameter( p, null );
results = q.getResultList();
assertEquals( 1, results.size() );
q = em.createQuery( "from Item i where i.intVal is null or i.intVal = ?" );
q = em.createQuery( "from Item i where i.intVal is null or i.intVal = ?1" );
q.setParameter( p, null );
results = q.getResultList();
assertEquals( 1, results.size() );
@ -216,7 +217,7 @@ public class QueryTest extends BaseEntityManagerFunctionalTestCase {
try {
Item item = new Item( "Mouse", "Micro$oft mouse" );
em.persist( item );
Query q = em.createQuery( "from Item i where i.intVal=?" );
Query q = em.createQuery( "from Item i where i.intVal=?1" );
Parameter p = new Parameter() {
@Override
public String getName() {
@ -225,7 +226,7 @@ public class QueryTest extends BaseEntityManagerFunctionalTestCase {
@Override
public Integer getPosition() {
return 0;
return 1;
}
@Override
@ -238,11 +239,11 @@ public class QueryTest extends BaseEntityManagerFunctionalTestCase {
List results = q.getResultList();
// null != null
assertEquals( 0, results.size() );
q = em.createQuery( "from Item i where i.intVal is null and ? is null" );
q = em.createQuery( "from Item i where i.intVal is null and ?1 is null" );
q.setParameter( p, null );
results = q.getResultList();
assertEquals( 1, results.size() );
q = em.createQuery( "from Item i where i.intVal is null or i.intVal = ?" );
q = em.createQuery( "from Item i where i.intVal is null or i.intVal = ?1" );
q.setParameter( p, null );
results = q.getResultList();
assertEquals( 1, results.size() );
@ -688,9 +689,8 @@ public class QueryTest extends BaseEntityManagerFunctionalTestCase {
Query query = em.createQuery( "from Item item where item.name =?1 or item.descr = ?1" );
Parameter p1 = query.getParameter( 1 );
Assert.assertNotNull( p1 );
// in 5.2, '?<position' parameters are named while '?' are position-based.
Assert.assertNotNull( p1.getName() );
Assert.assertNull( p1.getPosition() );
Assert.assertNotNull( p1.getPosition() );
Assert.assertNull( p1.getName() );
em.getTransaction().commit();
}
@ -745,7 +745,7 @@ public class QueryTest extends BaseEntityManagerFunctionalTestCase {
params.add( item.getName() );
params.add( item2.getName() );
// deprecated usage of positional parameter by String
q.setParameter( "1", params );
q.setParameter( 1, params );
result = q.getResultList();
assertNotNull( result );
assertEquals( 2, result.size() );
@ -807,7 +807,7 @@ public class QueryTest extends BaseEntityManagerFunctionalTestCase {
params.add( item.getName() );
params.add( item2.getName() );
// deprecated usage of positional parameter by String
q.setParameter( "1", params );
q.setParameter( 1, params );
result = q.getResultList();
assertNotNull( result );
assertEquals( 2, result.size() );
@ -1011,13 +1011,7 @@ public class QueryTest extends BaseEntityManagerFunctionalTestCase {
// next using jpa-style positional parameter, but as a name (which is how Hibernate core treats these
query = em.createQuery( "select w from Wallet w where w.brand = ?1" );
// deprecated usage of positional parameter by String
query.setParameter( "1", "Lacoste" );
w = (Wallet) query.getSingleResult();
assertNotNull( w );
// finally using hql-style positional parameter
query = em.createQuery( "select w from Wallet w where w.brand = ?" );
query.setParameter( 0, "Lacoste" );
query.setParameter( 1, "Lacoste" );
w = (Wallet) query.getSingleResult();
assertNotNull( w );
@ -1047,8 +1041,19 @@ public class QueryTest extends BaseEntityManagerFunctionalTestCase {
em.persist( w );
em.flush();
// using jpa-style, position index should match syntax '?<position'.
Query jpaQuery = em.createQuery( "select w from Wallet w where w.brand = ?1 and w.model = ?3" );
// Gaps are not allowed
try {
Query jpaQuery = em.createQuery( "select w from Wallet w where w.brand = ?1 and w.model = ?3" );
fail( "expecting error regarding gap in positional param labels" );
}
catch ( IllegalArgumentException e ) {
assertNotNull( e.getCause() );
assertTyping( QueryException.class, e.getCause() );
assertTrue( e.getCause().getMessage().contains( "gap" ) );
}
// using jpa-style, position index should match syntax '?<position>'.
Query jpaQuery = em.createQuery( "select w from Wallet w where w.brand = ?1" );
jpaQuery.setParameter( 1, "Lacoste" );
try {
jpaQuery.setParameter( 2, "Expensive" );
@ -1077,17 +1082,6 @@ public class QueryTest extends BaseEntityManagerFunctionalTestCase {
catch (Exception e) {
assertTyping( IllegalArgumentException.class, e );
}
// using hql-style, should be 0-based
Query hqlQuery = em.createQuery( "select w from Wallet w where w.brand = ? and w.model = ?" );
try {
hqlQuery.setParameter( 1, "Lacoste" );
hqlQuery.setParameter( 2, "Expensive" );
fail( "Should fail due to a user error in parameters" );
}
catch (Exception e) {
assertTyping( IllegalArgumentException.class, e );
}
}
finally {
if ( em.getTransaction() != null && em.getTransaction().isActive() ) {

View File

@ -92,7 +92,7 @@ public class Java5FeaturesTest extends BaseCoreFunctionalTestCase {
s.clear();
communityBid = (CommunityBid) s.createSQLQuery( "select {b.*} from Bid b where b.id = ?" )
.addEntity( "b", CommunityBid.class )
.setInteger( 0, communityBid.getId() ).uniqueResult();
.setInteger( 1, communityBid.getId() ).uniqueResult();
assertEquals( Starred.OK, communityBid.getCommunityNote() );
s.delete( communityBid );
tx.commit();

View File

@ -376,8 +376,8 @@ public class EnumeratedTypeTest extends BaseNonConfigCoreFunctionalTestCase {
assertEquals( resultList.get(1).getTrimmed(), Trimmed.B );
// ensure querying works
final Query query = s.createQuery("select e from EntityEnum e where e.trimmed=?");
query.setParameter( 0, Trimmed.A );
final Query query = s.createQuery("select e from EntityEnum e where e.trimmed=?1");
query.setParameter( 1, Trimmed.A );
resultList = query.list();
assertEquals( resultList.size(), 1 );
assertEquals( resultList.get(0).getTrimmed(), Trimmed.A );

View File

@ -57,13 +57,13 @@ public class LoaderWithInvalidQueryTest extends BaseEntityManagerFunctionalTestC
name = "invalid_sql",
query = "SELECT p " +
"FROM Person p " +
"WHERE p.id = ? and p.valid = true"
"WHERE p.id = ?1 and p.valid = true"
)
@NamedQuery(
name = "another_invalid_sql",
query = "SELECT p " +
"FROM _Person p " +
"WHERE p.id = ?"
"WHERE p.id = ?1"
)
public static class Person {

View File

@ -57,10 +57,10 @@ public class NamedQueryTest extends BaseCoreFunctionalTestCase {
}
@Test
public void testNamedQueriesOrdinalParametersAreZeroBased() {
public void testNamedQueriesOrdinalParametersAreOneBased() {
doInHibernate( this::sessionFactory, session -> {
Query query = session.getNamedQuery( "NamedQuery" );
query.setParameter( 0, GAME_TITLES[0] );
query.setParameter( 1, GAME_TITLES[0] );
List list = query.getResultList();
assertEquals( 1, list.size() );
}
@ -68,10 +68,10 @@ public class NamedQueryTest extends BaseCoreFunctionalTestCase {
}
@Test
public void testNativeNamedQueriesOrdinalParametersAreZeroBased() {
public void testNativeNamedQueriesOrdinalParametersAreOneBased() {
doInHibernate( this::sessionFactory, session -> {
Query query = session.getNamedNativeQuery( "NamedNativeQuery" );
query.setParameter( 0, GAME_TITLES[0] );
query.setParameter( 1, GAME_TITLES[0] );
List list = query.getResultList();
assertEquals( 1, list.size() );
}
@ -79,7 +79,7 @@ public class NamedQueryTest extends BaseCoreFunctionalTestCase {
}
@Entity(name = "Game")
@NamedQueries(@NamedQuery(name = "NamedQuery", query = "select g from Game g where title = ?"))
@NamedQueries(@NamedQuery(name = "NamedQuery", query = "select g from Game g where title = ?1"))
@NamedNativeQueries(@NamedNativeQuery(name = "NamedNativeQuery", query = "select * from Game g where title = ?"))
public static class Game {
private Long id;

View File

@ -351,7 +351,7 @@ public class QueryAndSQLTest extends BaseCoreFunctionalTestCase {
s.clear();
tx = s.beginTransaction();
Query q = s.getNamedQuery( "night.getAll.bySQL" );
q.setParameter( 0, 9990 );
q.setParameter( 1, 9990 );
List result = q.list();
assertEquals( 1, result.size() );
Night n2 = (Night) result.get( 0 );

View File

@ -59,9 +59,9 @@ public class ColumnTransformerTest extends BaseCoreFunctionalTestCase {
assertEquals(HEIGHT_INCHES, staff.getSizeInInches(), 0.01d);
// Test predicate and entity load via HQL
staff = (Staff)s.createQuery("from Staff s where s.sizeInInches between ? and ?")
.setDouble(0, HEIGHT_INCHES - 0.01d)
.setDouble(1, HEIGHT_INCHES + 0.01d)
staff = (Staff)s.createQuery("from Staff s where s.sizeInInches between ?1 and ?2")
.setDouble(1, HEIGHT_INCHES - 0.01d)
.setDouble(2, HEIGHT_INCHES + 0.01d)
.uniqueResult();
assertEquals(HEIGHT_INCHES, staff.getSizeInInches(), 0.01d);

View File

@ -247,9 +247,9 @@ public class ComponentTest extends BaseNonConfigCoreFunctionalTestCase {
assertEquals(HEIGHT_INCHES, u.getPerson().getHeightInches(), 0.01d);
// Test predicate and entity load via HQL
u = (User)s.createQuery("from User u where u.person.heightInches between ? and ?")
.setDouble(0, HEIGHT_INCHES - 0.01d)
.setDouble(1, HEIGHT_INCHES + 0.01d)
u = (User)s.createQuery("from User u where u.person.heightInches between ?1 and ?2")
.setDouble(1, HEIGHT_INCHES - 0.01d)
.setDouble(2, HEIGHT_INCHES + 0.01d)
.uniqueResult();
assertEquals(HEIGHT_INCHES, u.getPerson().getHeightInches(), 0.01d);

View File

@ -97,9 +97,9 @@ public class CompositeUserTypeTest extends BaseCoreFunctionalTestCase {
assertEquals(AMOUNT.doubleValue(), f.getHoldings().getAmount().doubleValue(), 0.01d);
// Test predicate and entity load via HQL
f = (MutualFund)s.createQuery("from MutualFund f where f.holdings.amount between ? and ?")
.setBigDecimal(0, AMOUNT.subtract(one))
.setBigDecimal(1, AMOUNT.add(one))
f = (MutualFund)s.createQuery("from MutualFund f where f.holdings.amount between ?1 and ?2")
.setBigDecimal(1, AMOUNT.subtract(one))
.setBigDecimal(2, AMOUNT.add(one))
.uniqueResult();
assertEquals(AMOUNT.doubleValue(), f.getHoldings().getAmount().doubleValue(), 0.01d);

View File

@ -209,7 +209,7 @@ public class DynamicFilterTest extends BaseNonConfigCoreFunctionalTestCase {
assertTrue( "Incorrect collectionfilter count", result.getOrders().size() == 1 );
log.info( "HQL against Product..." );
results = session.createQuery( "from Product as p where p.stockNumber = ?" ).setInteger( 0, 124 ).list();
results = session.createQuery( "from Product as p where p.stockNumber = ?1" ).setInteger( 1, 124 ).list();
assertTrue( results.size() == 1 );
session.close();
@ -392,23 +392,23 @@ public class DynamicFilterTest extends BaseNonConfigCoreFunctionalTestCase {
log.info("query against Department with a subquery on Salesperson in the APAC reqion...");
List departments = session.createQuery(
"select d from Department as d where d.id in (select s.department from Salesperson s where s.name = ?)"
).setString( 0, "steve" ).list();
"select d from Department as d where d.id in (select s.department from Salesperson s where s.name = ?1)"
).setString( 1, "steve" ).list();
assertEquals("Incorrect department count", 1, departments.size());
log.info("query against Department with a subquery on Salesperson in the FooBar reqion...");
session.enableFilter("region").setParameter( "region", "Foobar" );
departments = session.createQuery("select d from Department as d where d.id in (select s.department from Salesperson s where s.name = ?)").setString(0, "steve").list();
departments = session.createQuery("select d from Department as d where d.id in (select s.department from Salesperson s where s.name = ?1)").setString(1, "steve").list();
assertEquals( "Incorrect department count", 0, departments.size() );
log.info("query against Order with a subquery for line items with a subquery line items where the product name is Acme Hair Gel and the quantity is greater than 1 in a given region for a given buyer");
session.enableFilter("region").setParameter( "region", "APAC" );
List orders = session.createQuery("select o from Order as o where exists (select li.id from LineItem li, Product as p where p.id = li.product and li.quantity >= ? and p.name = ?) and o.buyer = ?")
.setLong(0, 1L).setString(1, "Acme Hair Gel").setString(2, "gavin").list();
List orders = session.createQuery("select o from Order as o where exists (select li.id from LineItem li, Product as p where p.id = li.product and li.quantity >= ?1 and p.name = ?2) and o.buyer = ?3")
.setLong(1, 1L).setString(2, "Acme Hair Gel").setString(3, "gavin").list();
assertEquals( "Incorrect orders count", 1, orders.size() );
@ -417,8 +417,8 @@ public class DynamicFilterTest extends BaseNonConfigCoreFunctionalTestCase {
session.enableFilter("region").setParameter("region", "APAC");
session.enableFilter("effectiveDate").setParameter( "asOfDate", testData.lastMonth.getTime() );
orders = session.createQuery("select o from Order as o where exists (select li.id from LineItem li where li.quantity >= ? and li.product in (select p.id from Product p where p.name = ?)) and o.buyer = ?")
.setLong(0, 1L).setString(1, "Acme Hair Gel").setString(2, "gavin").list();
orders = session.createQuery("select o from Order as o where exists (select li.id from LineItem li where li.quantity >= ?1 and li.product in (select p.id from Product p where p.name = ?2)) and o.buyer = ?3")
.setLong(1, 1L).setString(2, "Acme Hair Gel").setString(3, "gavin").list();
assertEquals( "Incorrect orders count", 1, orders.size() );
@ -430,8 +430,8 @@ public class DynamicFilterTest extends BaseNonConfigCoreFunctionalTestCase {
session.enableFilter("region").setParameter("region", "APAC");
session.enableFilter("effectiveDate").setParameter("asOfDate", testData.fourMonthsAgo.getTime());
orders = session.createQuery("select o from Order as o where exists (select li.id from LineItem li where li.quantity >= ? and li.product in (select p.id from Product p where p.name = ?)) and o.buyer = ?")
.setLong( 0, 1L ).setString( 1, "Acme Hair Gel" ).setString( 2, "gavin" ).list();
orders = session.createQuery("select o from Order as o where exists (select li.id from LineItem li where li.quantity >= ?1 and li.product in (select p.id from Product p where p.name = ?2)) and o.buyer = ?3")
.setLong(1, 1L).setString(2, "Acme Hair Gel").setString(3, "gavin").list();
assertEquals("Incorrect orders count", 0, orders.size());
@ -440,8 +440,8 @@ public class DynamicFilterTest extends BaseNonConfigCoreFunctionalTestCase {
session.enableFilter("region").setParameter("region", "APAC");
session.enableFilter("effectiveDate").setParameter("asOfDate", testData.lastMonth.getTime());
orders = session.createQuery("select o from Order as o where exists (select li.id from LineItem li where li.quantity >= :quantity and li.product in (select p.id from Product p where p.name = :name)) and o.buyer = :buyer")
.setLong("quantity", 1L).setString("name", "Acme Hair Gel").setString("buyer", "gavin").list();
orders = session.createQuery("select o from Order as o where exists (select li.id from LineItem li where li.quantity >= ?1 and li.product in (select p.id from Product p where p.name = ?2)) and o.buyer = ?3")
.setLong(1, 1L).setString(2, "Acme Hair Gel").setString(3, "gavin").list();
assertEquals("Incorrect orders count", 1, orders.size());
@ -450,8 +450,8 @@ public class DynamicFilterTest extends BaseNonConfigCoreFunctionalTestCase {
session.enableFilter("region").setParameter("region", "APAC");
session.enableFilter("effectiveDate").setParameter("asOfDate", testData.lastMonth.getTime());
orders = session.createQuery("select o from Order as o where exists (select li.id from LineItem li where li.quantity >= ? and li.product in (select p.id from Product p where p.name = ?)) and o.buyer = :buyer")
.setLong( 0, 1L ).setString( 1, "Acme Hair Gel" ).setString( "buyer", "gavin" ).list();
orders = session.createQuery("select o from Order as o where exists (select li.id from LineItem li where li.quantity >= ?1 and li.product in (select p.id from Product p where p.name = ?2)) and o.buyer = ?3")
.setLong(1, 1L).setString(2, "Acme Hair Gel").setString(3, "gavin").list();
assertEquals("Incorrect orders count", 1, orders.size());
@ -476,25 +476,25 @@ public class DynamicFilterTest extends BaseNonConfigCoreFunctionalTestCase {
Session session = openSession();
session.beginTransaction();
final String queryString = "from Order o where ? in ( select sp.name from Salesperson sp )";
final String queryString = "from Order o where ?1 in ( select sp.name from Salesperson sp )";
// first a control-group query
List result = session.createQuery( queryString ).setParameter( 0, "steve" ).list();
List result = session.createQuery( queryString ).setParameter( 1, "steve" ).list();
assertEquals( 2, result.size() );
// now lets enable filters on Order...
session.enableFilter( "fulfilledOrders" ).setParameter( "asOfDate", testData.lastMonth.getTime() );
result = session.createQuery( queryString ).setParameter( 0, "steve" ).list();
result = session.createQuery( queryString ).setParameter( 1, "steve" ).list();
assertEquals( 1, result.size() );
// now, lets additionally enable filter on Salesperson. First a valid one...
session.enableFilter( "regionlist" ).setParameterList( "regions", new String[] { "APAC" } );
result = session.createQuery( queryString ).setParameter( 0, "steve" ).list();
result = session.createQuery( queryString ).setParameter( 1, "steve" ).list();
assertEquals( 1, result.size() );
// ... then a silly one...
session.enableFilter( "regionlist" ).setParameterList( "regions", new String[] { "gamma quadrant" } );
result = session.createQuery( queryString ).setParameter( 0, "steve" ).list();
result = session.createQuery( queryString ).setParameter( 1, "steve" ).list();
assertEquals( 0, result.size() );
session.getTransaction().commit();

View File

@ -195,9 +195,9 @@ public class ASTParserLoadingTest extends BaseCoreFunctionalTestCase {
session.clear();
session.getTransaction().begin();
List result = session.createQuery( "FROM Zoo z WHERE z.name IN (?1) and z.address.city IN (?11)" )
.setParameterList( "1", namesArray )
.setParameterList( "11", citiesArray )
List result = session.createQuery( "FROM Zoo z WHERE z.name IN (?1) and z.address.city IN (?2)" )
.setParameterList( 1, namesArray )
.setParameterList( 2, citiesArray )
.list();
assertEquals( 1, result.size() );
session.getTransaction().commit();
@ -761,13 +761,13 @@ public class ASTParserLoadingTest extends BaseCoreFunctionalTestCase {
String query =
( getDialect() instanceof DB2Dialect || getDialect() instanceof HSQLDialect ) ?
"from Human where cast(? as string) is null" :
"from Human where ? is null"
"from Human where ?1 is null"
;
if ( getDialect() instanceof DerbyDialect ) {
s.createQuery( query ).setParameter( 0, "null" ).list();
s.createQuery( query ).setParameter( 1, "null" ).list();
}
else {
s.createQuery( query ).setParameter( 0, null ).list();
s.createQuery( query ).setParameter( 1, null ).list();
}
s.getTransaction().commit();
@ -1429,35 +1429,35 @@ public class ASTParserLoadingTest extends BaseCoreFunctionalTestCase {
params.add( "Doe" );
params.add( "Public" );
s.createQuery( "from Human where name.last in (?1)" )
.setParameterList( "1", params )
.setParameterList( 1, params )
.list();
s.createQuery( "from Human where name.last in ?1" )
.setParameterList( "1", params )
.setParameterList( 1, params )
.list();
s.createQuery( "from Human where nickName = ?1 and ( name.first = ?2 or name.last in (?3) )" )
.setParameter( "1", "Yogster" )
.setParameter( "2", "Yogi" )
.setParameterList( "3", params )
.setParameter( 1, "Yogster" )
.setParameter( 2, "Yogi" )
.setParameterList( 3, params )
.list();
s.createQuery( "from Human where nickName = ?1 and ( name.first = ?2 or name.last in ?3 )" )
.setParameter( "1", "Yogster" )
.setParameter( "2", "Yogi" )
.setParameterList( "3", params )
.setParameter( 1, "Yogster" )
.setParameter( 2, "Yogi" )
.setParameterList( 3, params )
.list();
s.createQuery( "from Human where nickName = ?1 or ( name.first = ?2 and name.last in (?3) )" )
.setParameter( "1", "Yogster" )
.setParameter( "2", "Yogi" )
.setParameterList( "3", params )
.setParameter( 1, "Yogster" )
.setParameter( 2, "Yogi" )
.setParameterList( 3, params )
.list();
s.createQuery( "from Human where nickName = ?1 or ( name.first = ?2 and name.last in ?3 )" )
.setParameter( "1", "Yogster" )
.setParameter( "2", "Yogi" )
.setParameterList( "3", params )
.setParameter( 1, "Yogster" )
.setParameter( 2, "Yogi" )
.setParameterList( 3, params )
.list();
s.getTransaction().commit();
@ -2489,9 +2489,9 @@ public class ASTParserLoadingTest extends BaseCoreFunctionalTestCase {
public void testParameterMixing() {
Session s = openSession();
Transaction t = s.beginTransaction();
s.createQuery( "from Animal a where a.description = ? and a.bodyWeight = ? or a.bodyWeight = :bw" )
.setString( 0, "something" )
.setFloat( 1, 12345f )
s.createQuery( "from Animal a where a.description = ?1 and a.bodyWeight = ?2 or a.bodyWeight = :bw" )
.setString( 1, "something" )
.setFloat( 2, 12345f )
.setFloat( "bw", 123f )
.list();
t.commit();
@ -2502,13 +2502,13 @@ public class ASTParserLoadingTest extends BaseCoreFunctionalTestCase {
public void testOrdinalParameters() {
Session s = openSession();
Transaction t = s.beginTransaction();
s.createQuery( "from Animal a where a.description = ? and a.bodyWeight = ?" )
.setString( 0, "something" )
.setFloat( 1, 123f )
s.createQuery( "from Animal a where a.description = ?1 and a.bodyWeight = ?2" )
.setString( 1, "something" )
.setFloat( 2, 123f )
.list();
s.createQuery( "from Animal a where a.bodyWeight in (?, ?)" )
.setFloat( 0, 999f )
.setFloat( 1, 123f )
s.createQuery( "from Animal a where a.bodyWeight in (?1, ?2)" )
.setFloat( 1, 999f )
.setFloat( 2, 123f )
.list();
t.commit();
s.close();
@ -2801,8 +2801,8 @@ public class ASTParserLoadingTest extends BaseCoreFunctionalTestCase {
session.flush();
assertEquals( session.createFilter( human.getFriends(), "" ).list().size(), 1 );
assertEquals( session.createFilter( human.getFriends(), "where this.bodyWeight > ?" ).setFloat( 0, 10f ).list().size(), 1 );
assertEquals( session.createFilter( human.getFriends(), "where this.bodyWeight < ?" ).setFloat( 0, 10f ).list().size(), 0 );
assertEquals( session.createFilter( human.getFriends(), "where this.bodyWeight > ?1" ).setFloat( 1, 10f ).list().size(), 1 );
assertEquals( session.createFilter( human.getFriends(), "where this.bodyWeight < ?1" ).setFloat( 1, 10f ).list().size(), 0 );
session.delete(human);
session.delete(friend);
@ -2835,17 +2835,22 @@ public class ASTParserLoadingTest extends BaseCoreFunctionalTestCase {
session.flush();
assertEquals( session.createFilter( human.getFriends(), "" ).list().size(), 1 );
assertEquals( session.createFilter( human.getFriends(), "where this.heightInches < ?" ).setDouble( 0, 51d ).list().size(), 1 );
assertEquals(
session.createFilter( human.getFriends(), "where this.heightInches > ?" )
.setDouble( 0, 51d )
.list()
.size(), 0
session.createFilter( human.getFriends(), "where this.heightInches < ?1" ).setDouble( 1, 51d ).list().size(),
1
);
assertEquals(
session.createFilter( human.getFriends(), "where this.heightInches between 49 and 51" ).list().size(), 1
session.createFilter( human.getFriends(), "where this.heightInches > ?1" ).setDouble( 1, 51d ).list().size(),
0
);
assertEquals(
session.createFilter( human.getFriends(), "where this.heightInches between 49 and 51" ).list().size(),
1
);
assertEquals(
session.createFilter( human.getFriends(), "where this.heightInches not between 49 and 51" ).list().size(),
0
);
assertEquals( session.createFilter( human.getFriends(), "where this.heightInches not between 49 and 51" ).list().size(), 0 );
session.delete( human );
session.delete( friend );

View File

@ -369,8 +369,8 @@ public class BulkManipulationTest extends BaseCoreFunctionalTestCase {
Car c = (Car) s.createQuery( "from Car where owner = 'Kirsten'" ).uniqueResult();
c.setOwner( "NotKirsten" );
assertEquals( 0, s.getNamedQuery( "native-delete-car" ).setString( 0, "Kirsten" ).executeUpdate() );
assertEquals( 1, s.getNamedQuery( "native-delete-car" ).setString( 0, "NotKirsten" ).executeUpdate() );
assertEquals( 0, s.getNamedQuery( "native-delete-car" ).setString( 1, "Kirsten" ).executeUpdate() );
assertEquals( 1, s.getNamedQuery( "native-delete-car" ).setString( 1, "NotKirsten" ).executeUpdate() );
assertEquals(

View File

@ -9,6 +9,7 @@ package org.hibernate.test.hql;
import org.junit.Test;
import org.hibernate.Session;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.cfg.Configuration;
import org.hibernate.cfg.Environment;
import org.hibernate.hql.internal.classic.ClassicQueryTranslatorFactory;
@ -26,6 +27,7 @@ public class ClassicTranslatorTest extends QueryTranslatorTestCase {
public void configure(Configuration cfg) {
super.configure( cfg );
cfg.setProperty( Environment.QUERY_TRANSLATOR, ClassicQueryTranslatorFactory.class.getName() );
cfg.setProperty( AvailableSettings.JDBC_TYLE_PARAMS_ZERO_BASE, "true" );
}
@Override
@ -52,10 +54,6 @@ public class ClassicTranslatorTest extends QueryTranslatorTestCase {
session.createQuery( "from Animal as a where a.description = ?" ).setString( 0, "jj" ).list();
session.createQuery( "from Animal as a where a.description = :desc" ).setString( "desc", "jr" ).list();
session.createQuery( "from Animal as a where a.description = ? or a.description = :desc" )
.setString( 0, "jj" )
.setString( "desc", "jr" )
.list();
session.getTransaction().commit();
session.close();

View File

@ -83,14 +83,14 @@ public class CollectionMapWithComponentValueTest extends BaseCoreFunctionalTestC
public void testMapKeyExpressionInWhere() {
doInHibernate( this::sessionFactory, s -> {
// JPA form
Query query = s.createQuery( "select te from TestEntity te join te.values v where ? in (key(v)) " );
query.setParameter( 0, keyValue );
Query query = s.createQuery( "select te from TestEntity te join te.values v where ?1 in (key(v)) " );
query.setParameter( 1, keyValue );
assertThat( query.list().size(), is( 1 ) );
// Hibernate additional form
query = s.createQuery( "select te from TestEntity te where ? in (key(te.values))" );
query.setParameter( 0, keyValue );
query = s.createQuery( "select te from TestEntity te where ?1 in (key(te.values))" );
query.setParameter( 1, keyValue );
assertThat( query.list().size(), is( 1 ) );
@ -113,7 +113,7 @@ public class CollectionMapWithComponentValueTest extends BaseCoreFunctionalTestC
fail( "HibernateException expected - Could not determine type for EmbeddableValue" );
}
catch ( Exception e ) {
assertTyping( HibernateException.class, e );
assertTyping( IllegalArgumentException.class, e );
}
// Hibernate additional form
@ -124,7 +124,7 @@ public class CollectionMapWithComponentValueTest extends BaseCoreFunctionalTestC
fail( "HibernateException expected - Could not determine type for EmbeddableValue" );
}
catch ( Exception e ) {
assertTyping( HibernateException.class, e );
assertTyping( IllegalArgumentException.class, e );
}
// Test value property dereference

View File

@ -70,12 +70,12 @@ public class EJBQLTest extends BaseCoreFunctionalTestCase {
QueryTranslatorImpl qt = compile( "from Animal a where a.bodyWeight = ?1" );
AST ast = ( AST ) qt.getSqlAST();
// make certain that the ejb3-positional param got recognized as a named param
// make certain that the ejb3-positional param got recognized as a positional param
List namedParams = ASTUtil.collectChildren(
ast,
new ASTUtil.FilterPredicate() {
public boolean exclude(AST n) {
return n.getType() != HqlSqlTokenTypes.NAMED_PARAM;
return n.getType() != HqlSqlTokenTypes.PARAM;
}
}
);

View File

@ -170,15 +170,15 @@ public class HQLTest extends QueryTranslatorTestCase {
} )
public void testRowValueConstructorSyntaxInInListBeingTranslated() {
QueryTranslatorImpl translator = createNewQueryTranslator("from LineItem l where l.id in (?)");
QueryTranslatorImpl translator = createNewQueryTranslator("from LineItem l where l.id in (?1)");
assertInExist("'in' should be translated to 'and'", false, translator);
translator = createNewQueryTranslator("from LineItem l where l.id in ?");
translator = createNewQueryTranslator("from LineItem l where l.id in ?1");
assertInExist("'in' should be translated to 'and'", false, translator);
translator = createNewQueryTranslator("from LineItem l where l.id in (('a1',1,'b1'),('a2',2,'b2'))");
assertInExist("'in' should be translated to 'and'", false, translator);
translator = createNewQueryTranslator("from Animal a where a.id in (?)");
translator = createNewQueryTranslator("from Animal a where a.id in (?1)");
assertInExist("only translated tuple has 'in' syntax", true, translator);
translator = createNewQueryTranslator("from Animal a where a.id in ?");
translator = createNewQueryTranslator("from Animal a where a.id in ?1");
assertInExist("only translated tuple has 'in' syntax", true, translator);
translator = createNewQueryTranslator("from LineItem l where l.id in (select a1 from Animal a1 left join a1.offspring o where a1.id = 1)");
assertInExist("do not translate sub-queries", true, translator);
@ -290,13 +290,13 @@ public class HQLTest extends QueryTranslatorTestCase {
assertEquals( "incorrect return type count", 1, translator.getReturnTypes().length );
assertEquals( "incorrect return type", CalendarDateType.INSTANCE, translator.getReturnTypes()[0] );
translator = createNewQueryTranslator( "from Order o where o.orderDate > ?" );
assertEquals( "incorrect expected param type", CalendarDateType.INSTANCE, translator.getParameterTranslations().getOrdinalParameterExpectedType( 0 ) );
translator = createNewQueryTranslator( "from Order o where o.orderDate > ?1" );
assertEquals( "incorrect expected param type", CalendarDateType.INSTANCE, translator.getParameterTranslations().getPositionalParameterInformation( 1 ).getExpectedType() );
translator = createNewQueryTranslator( "select o.orderDate + ? from Order o" );
translator = createNewQueryTranslator( "select o.orderDate + ?1 from Order o" );
assertEquals( "incorrect return type count", 1, translator.getReturnTypes().length );
assertEquals( "incorrect return type", CalendarDateType.INSTANCE, translator.getReturnTypes()[0] );
assertEquals( "incorrect expected param type", DoubleType.INSTANCE, translator.getParameterTranslations().getOrdinalParameterExpectedType( 0 ) );
assertEquals( "incorrect expected param type", DoubleType.INSTANCE, translator.getParameterTranslations().getPositionalParameterInformation( 1 ).getExpectedType() );
}
@ -1010,7 +1010,7 @@ public class HQLTest extends QueryTranslatorTestCase {
@Test
public void testImplicitJoins() throws Exception {
// Two dots...
assertTranslation( "from Animal an where an.mother.bodyWeight > ?" );
assertTranslation( "from Animal an where an.mother.bodyWeight > ?1" );
assertTranslation( "from Animal an where an.mother.bodyWeight > 10" );
assertTranslation( "from Dog dog where dog.mother.bodyWeight > 10" );
// Three dots...
@ -1220,7 +1220,7 @@ public class HQLTest extends QueryTranslatorTestCase {
@Test
public void testPositionalParameters() throws Exception {
assertTranslation( "from Animal an where an.bodyWeight > ?" );
assertTranslation( "from Animal an where an.bodyWeight > ?1" );
}
@Test

View File

@ -137,8 +137,8 @@ public class WithClauseTest extends BaseCoreFunctionalTestCase {
s.beginTransaction();
Query query = s.createQuery( "select a from SimpleEntityWithAssociation as e INNER JOIN e.associatedEntities as a WITH e.name=?" );
query.setParameter( 0, "entity1" );
Query query = s.createQuery( "select a from SimpleEntityWithAssociation as e INNER JOIN e.associatedEntities as a WITH e.name=?1" );
query.setParameter( 1, "entity1" );
List list = query.list();
assertEquals( list.size(), 1 );

View File

@ -15,6 +15,8 @@ import java.util.List;
import org.hibernate.Hibernate;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.cfg.Configuration;
import org.hibernate.criterion.Restrictions;
import org.hibernate.jdbc.AbstractWork;
@ -23,6 +25,7 @@ import org.junit.Test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
/**
@ -34,6 +37,12 @@ public class JoinTest extends BaseCoreFunctionalTestCase {
return new String[] { "join/Person.hbm.xml" };
}
@Override
protected void configure(Configuration configuration) {
super.afterConfigurationBuilt( configuration );
configuration.setProperty( AvailableSettings.JDBC_TYLE_PARAMS_ZERO_BASE, "true" );
}
@Test
public void testSequentialSelects() {
Session s = openSession();
@ -197,14 +206,15 @@ public class JoinTest extends BaseCoreFunctionalTestCase {
assertEquals(PASSWORD_EXPIRY_DAYS, u.getPasswordExpiryDays(), 0.01d);
// Test predicate and entity load via HQL
p = (Person)s.createQuery("from Person p where p.heightInches between ? and ?")
.setDouble(0, HEIGHT_INCHES - 0.01d)
.setDouble(1, HEIGHT_INCHES + 0.01d)
p = (Person)s.createQuery("from Person p where p.heightInches between ?1 and ?2")
.setDouble(1, HEIGHT_INCHES - 0.01d)
.setDouble(2, HEIGHT_INCHES + 0.01d)
.uniqueResult();
assertNotNull( p );
assertEquals(HEIGHT_INCHES, p.getHeightInches(), 0.01d);
u = (User)s.createQuery("from User u where u.passwordExpiryDays between ? and ?")
.setDouble(0, PASSWORD_EXPIRY_DAYS - 0.01d)
.setDouble(1, PASSWORD_EXPIRY_DAYS + 0.01d)
u = (User)s.createQuery("from User u where u.passwordExpiryDays between ?1 and ?2")
.setDouble(1, PASSWORD_EXPIRY_DAYS - 0.01d)
.setDouble(2, PASSWORD_EXPIRY_DAYS + 0.01d)
.uniqueResult();
assertEquals(PASSWORD_EXPIRY_DAYS, u.getPasswordExpiryDays(), 0.01d);

View File

@ -214,7 +214,7 @@ public class JoinedSubclassTest extends BaseCoreFunctionalTestCase {
assertEquals(HEIGHT_CENTIMETERS, heightViaSql, 0.01d);
Double expiryViaSql =
( (Number)s.createSQLQuery("select pwd_expiry_weeks from JEmployee where person_id=?")
.setLong(0, e.getId())
.setLong(1, e.getId())
.uniqueResult()
).doubleValue();
assertEquals(PASSWORD_EXPIRY_WEEKS, expiryViaSql, 0.01d);
@ -236,14 +236,14 @@ public class JoinedSubclassTest extends BaseCoreFunctionalTestCase {
assertEquals(PASSWORD_EXPIRY_DAYS, e.getPasswordExpiryDays(), 0.01d);
// Test predicate and entity load via HQL
p = (Person)s.createQuery("from Person p where p.heightInches between ? and ?")
.setDouble(0, HEIGHT_INCHES - 0.01d)
.setDouble(1, HEIGHT_INCHES + 0.01d)
p = (Person)s.createQuery("from Person p where p.heightInches between ?1 and ?2")
.setDouble(1, HEIGHT_INCHES - 0.01d)
.setDouble(2, HEIGHT_INCHES + 0.01d)
.uniqueResult();
assertEquals(HEIGHT_INCHES, p.getHeightInches(), 0.01d);
e = (Employee)s.createQuery("from Employee e where e.passwordExpiryDays between ? and ?")
.setDouble(0, PASSWORD_EXPIRY_DAYS - 0.01d)
.setDouble(1, PASSWORD_EXPIRY_DAYS + 0.01d)
e = (Employee)s.createQuery("from Employee e where e.passwordExpiryDays between ?1 and ?2")
.setDouble(1, PASSWORD_EXPIRY_DAYS - 0.01d)
.setDouble(2, PASSWORD_EXPIRY_DAYS + 0.01d)
.uniqueResult();
assertEquals(PASSWORD_EXPIRY_DAYS, e.getPasswordExpiryDays(), 0.01d);
@ -257,7 +257,7 @@ public class JoinedSubclassTest extends BaseCoreFunctionalTestCase {
assertEquals(2.54d, heightViaSql, 0.01d);
expiryViaSql =
( (Number)s.createSQLQuery("select pwd_expiry_weeks from JEmployee where person_id=?")
.setLong(0, e.getId())
.setLong(1, e.getId())
.uniqueResult()
).doubleValue();
assertEquals(1d, expiryViaSql, 0.01d);

View File

@ -0,0 +1,39 @@
/*
* 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.test.jpa.compliance;
import javax.persistence.Parameter;
import org.hibernate.query.Query;
import org.hibernate.testing.transaction.TransactionUtil2;
import org.hibernate.test.jpa.AbstractJPATest;
import org.junit.Test;
import static org.hamcrest.CoreMatchers.either;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.notNullValue;
import static org.hamcrest.MatcherAssert.assertThat;
/**
* @author Steve Ebersole
*/
public class JpaPositionalParameterTest extends AbstractJPATest {
@Test
public void testPositionalParameters() {
TransactionUtil2.inTransaction(
sessionFactory(),
session -> {
Query query = session.createQuery( "select i from Item i where name = ?1 or name = ?2" );
for ( Parameter<?> parameter : query.getParameters() ) {
assertThat( parameter.getPosition(), notNullValue() );
assertThat( parameter.getPosition(), either( is(1) ).or( is(2) ) );
}
}
);
}
}

View File

@ -19,7 +19,7 @@ public class NativeQueryTest extends AbstractJPATest {
public void testJpaStylePositionalParametersInNativeSql() {
Session s = openSession();
s.beginTransaction();
s.createSQLQuery( "select NAME from EJB3_ITEM where ITEM_ID = ?1" ).setParameter( "1", new Long( 123 ) ).list();
s.createSQLQuery( "select NAME from EJB3_ITEM where ITEM_ID = ?1" ).setParameter( 1, 123L ).list();
s.getTransaction().commit();
s.close();
}

Some files were not shown because too many files have changed in this diff Show More