Changed positional parameter mappings to be stored as list.

Added JPA positional and named parameter translation support.
This commit is contained in:
Chris Cranford 2016-04-28 20:53:23 -05:00 committed by Steve Ebersole
parent 8ef423a1b3
commit 35f27be589
1 changed files with 80 additions and 76 deletions

View File

@ -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<QueryParameter, QueryParameterBinding> parameterBindingMap;
private Map<QueryParameter, QueryParameterListBinding> parameterListBindingMap;
private Map<QueryParameter, QueryParameterBinding> positionParameterBindingMap;
private List<QueryParameterBinding> 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<QueryParameter<?>> queryParameters) {
public QueryParameterBindingsImpl(SessionFactoryImplementor sessionFactory,
Set<QueryParameter<?>> 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<Map.Entry<QueryParameter,QueryParameterBinding>> entryIterator = parameterBindingMap.entrySet().iterator();
final Iterator<Map.Entry<QueryParameter, QueryParameterBinding>> entryIterator = parameterBindingMap.entrySet().iterator();
while ( entryIterator.hasNext() ) {
final Map.Entry<QueryParameter,QueryParameterBinding> entry = entryIterator.next();
final Map.Entry<QueryParameter, QueryParameterBinding> 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<QueryParameter, QueryParameterBinding> entry = locateBindingEntry( position );
if ( entry != null ) {
return entry.getValue();
if ( position < positionalParameterBindings.size() ) {
return positionalParameterBindings.get( position );
}
return null;
}
protected Map.Entry<QueryParameter, QueryParameterBinding> locateBindingEntry(int position) {
for ( Map.Entry<QueryParameter, QueryParameterBinding> entry : parameterBindingMap.entrySet() ) {
if ( entry.getKey().getPosition() != null && position == entry.getKey().getPosition() ) {
return entry;
}
}
return null;
}
protected Map.Entry<QueryParameter, QueryParameterBinding> locatePositionBinding(int position) {
for ( Map.Entry<QueryParameter, QueryParameterBinding> 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<QueryParameter, QueryParameterBinding> 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<QueryParameter, QueryParameterBinding> 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<Integer,QueryParameterBinding> positionalParameterBindingMap = collectPositionalParameterBindings();
TreeMap<Integer, QueryParameterBinding> 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<Integer, QueryParameterBinding> collectPositionalParameterBindings() {
final TreeMap<Integer, QueryParameterBinding> bindings = new TreeMap<>();
for ( Map.Entry<QueryParameter, QueryParameterBinding> 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<Integer,QueryParameterBinding> positionalParameterBindingMap = collectPositionalParameterBindings();
TreeMap<Integer, QueryParameterBinding> 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<String, TypedValue> collectNamedParameterBindings() {
Map<String,TypedValue> collectedBindings = new HashMap<>();
Map<String, TypedValue> collectedBindings = new HashMap<>();
for ( Map.Entry<QueryParameter, QueryParameterBinding> entry : parameterBindingMap.entrySet() ) {
if ( entry.getKey().getName() == null ) {
continue;
@ -449,7 +453,7 @@ public class QueryParameterBindingsImpl implements QueryParameterBindings {
*/
@Deprecated
@SuppressWarnings("unchecked")
private <T> QueryParameterBinding<T> getAndRemoveBinding(QueryParameter<T> parameter) {
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 );
@ -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( ", " );
}