From 35f27be5899d8bbe27b3e53646bf783e812dc9cf Mon Sep 17 00:00:00 2001 From: Chris Cranford Date: Thu, 28 Apr 2016 20:53:23 -0500 Subject: [PATCH] Changed positional parameter mappings to be stored as list. Added JPA positional and named parameter translation support. --- .../internal/QueryParameterBindingsImpl.java | 156 +++++++++--------- 1 file changed, 80 insertions(+), 76 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/query/internal/QueryParameterBindingsImpl.java b/hibernate-core/src/main/java/org/hibernate/query/internal/QueryParameterBindingsImpl.java index ffe77b119d..ce265f18de 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/internal/QueryParameterBindingsImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/query/internal/QueryParameterBindingsImpl.java @@ -6,10 +6,12 @@ */ package org.hibernate.query.internal; +import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; +import java.util.List; import java.util.Map; import java.util.Set; import java.util.TreeMap; @@ -47,27 +49,32 @@ public class QueryParameterBindingsImpl implements QueryParameterBindings { private static final CoreMessageLogger log = CoreLogging.messageLogger( QueryParameterBindingsImpl.class ); private final SessionFactoryImplementor sessionFactory; + private final ParameterMetadata parameterMetadata; private Map parameterBindingMap; private Map parameterListBindingMap; - private Map positionParameterBindingMap; + private List positionalParameterBindings; - public static QueryParameterBindingsImpl from(ParameterMetadata parameterMetadata, SessionFactoryImplementor sessionFactory) { + public static QueryParameterBindingsImpl from(ParameterMetadata parameterMetadata, + SessionFactoryImplementor sessionFactory) { if ( parameterMetadata == null ) { - return new QueryParameterBindingsImpl( sessionFactory ); + return new QueryParameterBindingsImpl( sessionFactory, parameterMetadata ); } else { - return new QueryParameterBindingsImpl( sessionFactory, parameterMetadata.collectAllParameters() ); + return new QueryParameterBindingsImpl( sessionFactory, parameterMetadata.collectAllParameters(), parameterMetadata ); } } - public QueryParameterBindingsImpl(SessionFactoryImplementor sessionFactory) { - this( sessionFactory, Collections.emptySet() ); + public QueryParameterBindingsImpl(SessionFactoryImplementor sessionFactory, ParameterMetadata parameterMetadata) { + this( sessionFactory, Collections.emptySet(), parameterMetadata ); } - public QueryParameterBindingsImpl(SessionFactoryImplementor sessionFactory, Set> queryParameters) { + public QueryParameterBindingsImpl(SessionFactoryImplementor sessionFactory, + Set> queryParameters, + ParameterMetadata parameterMetadata) { this.sessionFactory = sessionFactory; - this.positionParameterBindingMap = new HashMap<>(); + this.parameterMetadata = parameterMetadata; + this.positionalParameterBindings = new ArrayList<>( 4 ); if ( queryParameters == null || queryParameters.isEmpty() ) { parameterBindingMap = Collections.emptyMap(); @@ -76,7 +83,11 @@ public class QueryParameterBindingsImpl implements QueryParameterBindings { parameterBindingMap = new HashMap<>(); for ( QueryParameter queryParameter : queryParameters ) { - parameterBindingMap.put( queryParameter, makeBinding( queryParameter ) ); + 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 ) ); + } } } @@ -155,9 +166,9 @@ public class QueryParameterBindingsImpl implements QueryParameterBindings { } protected QueryParameterBinding locateAndRemoveBinding(String name) { - final Iterator> entryIterator = parameterBindingMap.entrySet().iterator(); + final Iterator> entryIterator = parameterBindingMap.entrySet().iterator(); while ( entryIterator.hasNext() ) { - final Map.Entry entry = entryIterator.next(); + final Map.Entry entry = entryIterator.next(); if ( name.equals( entry.getKey().getName() ) ) { entryIterator.remove(); return entry.getValue(); @@ -168,28 +179,10 @@ public class QueryParameterBindingsImpl implements QueryParameterBindings { } protected QueryParameterBinding locateBinding(int position) { - Map.Entry entry = locateBindingEntry( position ); - if ( entry != null ) { - return entry.getValue(); + if ( position < positionalParameterBindings.size() ) { + return positionalParameterBindings.get( position ); } - return null; - } - protected Map.Entry locateBindingEntry(int position) { - for ( Map.Entry entry : parameterBindingMap.entrySet() ) { - if ( entry.getKey().getPosition() != null && position == entry.getKey().getPosition() ) { - return entry; - } - } - return null; - } - - protected Map.Entry locatePositionBinding(int position) { - for ( Map.Entry entry : positionParameterBindingMap.entrySet() ) { - if ( entry.getKey().getPosition() != null && position == entry.getKey().getPosition() ) { - return entry; - } - } return null; } @@ -203,20 +196,45 @@ public class QueryParameterBindingsImpl implements QueryParameterBindings { } public QueryParameterBinding getBinding(int position) { - // locate cached binding by position (cached only after getBinding(int) called) - Map.Entry bindingEntry = locatePositionBinding( position ); - if ( bindingEntry == null ) { - bindingEntry = locateBindingEntry( position ); - // cache the bind position mapping - positionParameterBindingMap.put( bindingEntry.getKey(), bindingEntry.getValue() ); + final boolean isJpaBootstrap = sessionFactory.getSessionFactoryOptions().isJpaBootstrap(); + + QueryParameterBinding binding = null; + if ( parameterMetadata != null ) { + if ( isJpaBootstrap && parameterMetadata.getPositionalParameterCount() == 0 ) { + // no positional parameters, assume jpa named. + binding = locateBinding( String.valueOf( position ) ); + } + else { + final int positionIndex = ( isJpaBootstrap ? position - 1 : position ); + final int parameterIndex = ( isJpaBootstrap ? position : position + 1 ); + + if ( positionIndex < positionalParameterBindings.size() ) { + binding = positionalParameterBindings.get( positionIndex ); + if ( binding == null ) { + // metadata parameters are 1-based + binding = makeBinding( parameterMetadata.getQueryParameter( parameterIndex ) ); + positionalParameterBindings.set( positionIndex, binding ); + } + } + else { + for ( int i = 0; i < positionIndex - positionalParameterBindings.size(); i++ ) { + positionalParameterBindings.add( null ); + } + // metadata parameters are 1-based + QueryParameter queryParameter = parameterMetadata.getQueryParameter( parameterIndex ); + binding = makeBinding( queryParameter ); + positionalParameterBindings.add( binding ); + } + } } - if ( bindingEntry == null ) { + if ( binding == null ) { throw new IllegalArgumentException( "Unknown parameter position: " + position ); } - return bindingEntry.getValue(); + return binding; } public void verifyParametersBound(boolean reserveFirstParameter) { + // verify named parameters bound for ( Map.Entry bindEntry : parameterBindingMap.entrySet() ) { if ( bindEntry.getKey().getPosition() == null ) { if ( !bindEntry.getValue().isBound() ) { @@ -229,10 +247,16 @@ public class QueryParameterBindingsImpl implements QueryParameterBindings { } } } - - final int positionalValueSpan = getPositionalValueSpan( reserveFirstParameter ); - final int positionCounts = getPositionalParameterCount(); - + // verify position parameters bound + for ( int i = 0; i < positionalParameterBindings.size(); i++ ) { + final QueryParameterBinding binding = positionalParameterBindings.get( i ); + if ( binding == null || !binding.isBound() ) { + throw new QueryException( "Positional parameter [" + i + "] not set" ); + } + } + // 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( @@ -251,33 +275,21 @@ public class QueryParameterBindingsImpl implements QueryParameterBindings { } } - private int getPositionalValueSpan(boolean reserveFirstParameter) { + private int calculatePositionalValueSpan(boolean reserveFirstParameter) { int positionalValueSpan = 0; - for ( QueryParameterBinding binding : positionParameterBindingMap.values() ) { + for ( QueryParameterBinding binding : positionalParameterBindings ) { if ( binding.isBound() ) { Type bindType = binding.getBindType(); - if ( bindType != null ) { - Object object = binding.getBindValue(); - positionalValueSpan += bindType.getColumnSpan( sessionFactory ); - } - else { - positionalValueSpan += 1; + if ( bindType == null ) { + bindType = SerializableType.INSTANCE; } + Object object = binding.getBindValue(); + positionalValueSpan += bindType.getColumnSpan( sessionFactory ); } } return positionalValueSpan; } - private int getPositionalParameterCount() { - int count = 0; - for( QueryParameter queryParameter : parameterBindingMap.keySet() ) { - if ( queryParameter.getPosition() != null ) { - count++; - } - } - return count; - } - /** * @deprecated (since 5.2) expect a different approach to org.hibernate.engine.spi.QueryParameters in 6.0 */ @@ -305,7 +317,7 @@ public class QueryParameterBindingsImpl implements QueryParameterBindings { */ @Deprecated public Type[] collectPositionalBindTypes() { - TreeMap positionalParameterBindingMap = collectPositionalParameterBindings(); + TreeMap positionalParameterBindingMap = collectPositionalParameterBindings(); Type[] types = new Type[ positionalParameterBindingMap.size() ]; // NOTE : bindings should be ordered by position by nature of a TreeMap... @@ -328,16 +340,8 @@ public class QueryParameterBindingsImpl implements QueryParameterBindings { private TreeMap collectPositionalParameterBindings() { final TreeMap bindings = new TreeMap<>(); - - for ( Map.Entry entry : parameterBindingMap.entrySet() ) { - if ( entry.getKey().getPosition() == null ) { - continue; - } - // only add those which have been cached - if ( positionParameterBindingMap.containsKey( entry.getKey() ) ) { - // these should be contiguous - bindings.put( entry.getKey().getPosition(), entry.getValue() ); - } + for ( int i = 0; i < positionalParameterBindings.size(); i++ ) { + bindings.put( i, positionalParameterBindings.get( i ) ); } return bindings; @@ -348,7 +352,7 @@ public class QueryParameterBindingsImpl implements QueryParameterBindings { */ @Deprecated public Object[] collectPositionalBindValues() { - TreeMap positionalParameterBindingMap = collectPositionalParameterBindings(); + TreeMap positionalParameterBindingMap = collectPositionalParameterBindings(); Object[] values = new Object[ positionalParameterBindingMap.size() ]; // NOTE : bindings should be ordered by position by nature of a TreeMap... @@ -367,7 +371,7 @@ public class QueryParameterBindingsImpl implements QueryParameterBindings { */ @Deprecated public Map collectNamedParameterBindings() { - Map collectedBindings = new HashMap<>(); + Map collectedBindings = new HashMap<>(); for ( Map.Entry entry : parameterBindingMap.entrySet() ) { if ( entry.getKey().getName() == null ) { continue; @@ -449,7 +453,7 @@ public class QueryParameterBindingsImpl implements QueryParameterBindings { */ @Deprecated @SuppressWarnings("unchecked") - private QueryParameterBinding getAndRemoveBinding(QueryParameter parameter) { + private QueryParameterBinding getAndRemoveBinding(QueryParameter parameter) { // see if this exact instance is known as a key if ( parameterBindingMap.containsKey( parameter ) ) { return parameterBindingMap.remove( parameter ); @@ -570,7 +574,7 @@ public class QueryParameterBindingsImpl implements QueryParameterBindings { // 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; + final String syntheticName = ( isJpaPositionalParam ? 'x' : "" ) + sourceParam.getName() + '_' + i; if ( i > 0 ) { expansionList.append( ", " ); }