diff --git a/hibernate-core/src/main/java/org/hibernate/engine/internal/AbstractTransactionCompletionProcessQueue.java b/hibernate-core/src/main/java/org/hibernate/engine/internal/AbstractTransactionCompletionProcessQueue.java index a5b706b9c107..ac785e78ceb9 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/internal/AbstractTransactionCompletionProcessQueue.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/internal/AbstractTransactionCompletionProcessQueue.java @@ -18,8 +18,8 @@ */ abstract class AbstractTransactionCompletionProcessQueue { SharedSessionContractImplementor session; - // Concurrency handling required when transaction completion process is dynamically registered - // inside event listener (HHH-7478). + // Concurrency handling required when the transaction completion process + // is dynamically registered inside an event listener (HHH-7478). ConcurrentLinkedQueue<@NonNull T> processes = new ConcurrentLinkedQueue<>(); AbstractTransactionCompletionProcessQueue(SharedSessionContractImplementor session) { diff --git a/hibernate-core/src/main/java/org/hibernate/engine/internal/AfterTransactionCompletionProcessQueue.java b/hibernate-core/src/main/java/org/hibernate/engine/internal/AfterTransactionCompletionProcessQueue.java index daf2ba616261..8a9a3f57024c 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/internal/AfterTransactionCompletionProcessQueue.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/internal/AfterTransactionCompletionProcessQueue.java @@ -5,17 +5,16 @@ package org.hibernate.engine.internal; import org.hibernate.HibernateException; -import org.hibernate.action.internal.BulkOperationCleanupAction; +import org.hibernate.action.internal.BulkOperationCleanupAction.BulkOperationCleanUpAfterTransactionCompletionProcess; import org.hibernate.cache.CacheException; -import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.engine.spi.TransactionCompletionCallbacks.AfterCompletionCallback; import java.util.HashSet; -import java.util.Iterator; import java.util.Set; import static org.hibernate.internal.CoreMessageLogger.CORE_LOGGER; +import static org.hibernate.internal.util.collections.ArrayHelper.EMPTY_STRING_ARRAY; /** * Encapsulates behavior needed for after transaction processing @@ -41,59 +40,54 @@ boolean hasActions() { void afterTransactionCompletion(boolean success) { AfterCompletionCallback process; while ( (process = processes.poll()) != null ) { - try { - process.doAfterTransactionCompletion( success, session ); - } - catch (CacheException ce) { - CORE_LOGGER.unableToReleaseCacheLock( ce ); - // continue loop - } - catch (Exception e) { - throw new HibernateException( - "Unable to perform afterTransactionCompletion callback: " + e.getMessage(), e ); - } + callAfterCompletion( success, process ); } + invalidateCaches(); + } - final SessionFactoryImplementor factory = session.getFactory(); - if ( factory.getSessionFactoryOptions().isQueryCacheEnabled() ) { - factory.getCache().getTimestampsCache() - .invalidate( querySpacesToInvalidate.toArray( new String[0] ), session ); + void executePendingBulkOperationCleanUpActions() { + if ( performBulkOperationCallbacks() ) { + invalidateCaches(); } - querySpacesToInvalidate.clear(); } - void executePendingBulkOperationCleanUpActions() { + private boolean performBulkOperationCallbacks() { boolean hasPendingBulkOperationCleanUpActions = false; - Iterator iterator = processes.iterator(); + var iterator = processes.iterator(); while ( iterator.hasNext() ) { - AfterCompletionCallback process = iterator.next(); - if ( process instanceof BulkOperationCleanupAction.BulkOperationCleanUpAfterTransactionCompletionProcess ) { - try { - hasPendingBulkOperationCleanUpActions = true; - process.doAfterTransactionCompletion( true, session ); + var process = iterator.next(); + if ( process instanceof BulkOperationCleanUpAfterTransactionCompletionProcess ) { + hasPendingBulkOperationCleanUpActions = true; + if ( callAfterCompletion( true, process ) ) { iterator.remove(); } - catch (CacheException ce) { - CORE_LOGGER.unableToReleaseCacheLock( ce ); - // continue loop - } - catch (Exception e) { - throw new HibernateException( - "Unable to perform afterTransactionCompletion callback: " + e.getMessage(), - e - ); - } } } + return hasPendingBulkOperationCleanUpActions; + } - if ( hasPendingBulkOperationCleanUpActions ) { - if ( session.getFactory().getSessionFactoryOptions().isQueryCacheEnabled() ) { - session.getFactory().getCache().getTimestampsCache().invalidate( - querySpacesToInvalidate.toArray( new String[0] ), - session - ); - } - querySpacesToInvalidate.clear(); + private boolean callAfterCompletion(boolean success, AfterCompletionCallback process) { + try { + process.doAfterTransactionCompletion( success, session ); + return true; + } + catch (CacheException ce) { + CORE_LOGGER.unableToReleaseCacheLock( ce ); + // continue loop + return false; } + catch (Exception e) { + throw new HibernateException( + "Unable to perform afterTransactionCompletion callback: " + e.getMessage(), e ); + } + } + + private void invalidateCaches() { + final var factory = session.getFactory(); + if ( factory.getSessionFactoryOptions().isQueryCacheEnabled() ) { + factory.getCache().getTimestampsCache(). + invalidate( querySpacesToInvalidate.toArray( EMPTY_STRING_ARRAY ), session ); + } + querySpacesToInvalidate.clear(); } } diff --git a/hibernate-core/src/main/java/org/hibernate/engine/internal/StatefulPersistenceContext.java b/hibernate-core/src/main/java/org/hibernate/engine/internal/StatefulPersistenceContext.java index 1470d7afd928..ebe819c7f5ab 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/internal/StatefulPersistenceContext.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/internal/StatefulPersistenceContext.java @@ -89,20 +89,15 @@ class StatefulPersistenceContext implements PersistenceContext { private static final int INIT_COLL_SIZE = 8; - /* - Eagerly Initialized Fields - the following fields are used in all circumstances, and are not worth (or not suited) to being converted into lazy - */ + // Eagerly initialized fields. The following fields are used in every circumstance + // and are not worth (or not suited) to being converted to lazy initialization. + private final SharedSessionContractImplementor session; private EntityEntryContext entityEntryContext; - /* - Everything else below should be carefully initialized only on first need; - this optimisation is very effective as null checks are free, while allocation costs - are very often the dominating cost of an application using ORM. - This is not general advice, but it's worth the added maintenance burden in this case - as this is a very central component of our library. - */ + // Everything else below should be carefully initialized only on first need. + // This optimization is very effective as null checks are free, while allocation + // costs are very often the dominating cost of an application using ORM. // Loaded entity instances, by EntityKey private HashMap entitiesByKey; @@ -113,8 +108,7 @@ the following fields are used in all circumstances, and are not worth (or not su // Loaded entity instances, by EntityUniqueKey private HashMap entitiesByUniqueKey; - - // Snapshots of current database state for entities + // Snapshots of the current database state for entities // that have *not* been loaded private HashMap entitySnapshotsByKey; @@ -136,7 +130,7 @@ the following fields are used in all circumstances, and are not worth (or not su // Set of EntityKeys of deleted unloaded proxies private HashSet deletedUnloadedEntityKeys; - // properties that we have tried to load, and not found in the database + // properties that we have tried to load and not found in the database private HashSet nullAssociations; // A list of collection wrappers that were instantiating during result set @@ -209,14 +203,6 @@ public boolean hasLoadContext() { return loadContexts != null; } -// @Override -// public void addUnownedCollection(CollectionKey key, PersistentCollection collection) { -// if ( unownedCollections == null ) { -// unownedCollections = CollectionHelper.mapOfSize( INIT_COLL_SIZE ); -// } -// unownedCollections.put( key, collection ); -// } -// @Override public PersistentCollection useUnownedCollection(CollectionKey key) { return unownedCollections == null ? null : unownedCollections.remove( key ); @@ -935,7 +921,7 @@ else if ( ownerPersister.isInstance( key ) ) { } else { // b) try by EntityKey, which means we need to resolve owner-key -> collection-key - // IMPL NOTE : yes if we get here this impl is very non-performant, but PersistenceContext + // IMPL NOTE: yes if we get here this impl is very non-performant, but PersistenceContext // was never designed to handle this case; adding that capability for real means splitting // the notions of: // 1) collection key @@ -1228,14 +1214,6 @@ public Object removeProxy(EntityKey key) { return removeProxyByKey( key ); } -// @Override -// public HashSet getNullifiableEntityKeys() { -// if ( nullifiableEntityKeys == null ) { -// nullifiableEntityKeys = new HashSet<>(); -// } -// return nullifiableEntityKeys; -// } - /** * @deprecated this will be removed: it provides too wide access, making it hard to optimise the internals * for specific access needs. Consider using #iterateEntities instead. diff --git a/hibernate-core/src/main/java/org/hibernate/procedure/internal/ProcedureParameterImpl.java b/hibernate-core/src/main/java/org/hibernate/procedure/internal/ProcedureParameterImpl.java index 9bb664d6874d..c8244612676a 100644 --- a/hibernate-core/src/main/java/org/hibernate/procedure/internal/ProcedureParameterImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/procedure/internal/ProcedureParameterImpl.java @@ -178,8 +178,7 @@ private BindableType getBindableType(QueryParameterBinding binding) { return type; } else if ( binding != null ) { - //noinspection unchecked - return (BindableType) binding.getBindType(); + return binding.getBindType(); } else { return null; diff --git a/hibernate-core/src/main/java/org/hibernate/query/internal/QueryParameterBindingImpl.java b/hibernate-core/src/main/java/org/hibernate/query/internal/QueryParameterBindingImpl.java index ba9f4cf55de0..3040f24cb964 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/internal/QueryParameterBindingImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/query/internal/QueryParameterBindingImpl.java @@ -4,14 +4,18 @@ */ package org.hibernate.query.internal; +import java.util.ArrayList; import java.util.Collection; +import java.util.List; +import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; import org.hibernate.HibernateException; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.metamodel.mapping.BasicValuedMapping; import org.hibernate.metamodel.mapping.MappingModelExpressible; import org.hibernate.metamodel.mapping.ModelPart; +import org.hibernate.query.QueryArgumentException; import org.hibernate.type.BindableType; import org.hibernate.query.QueryParameter; import org.hibernate.query.spi.QueryParameterBinding; @@ -115,7 +119,10 @@ public T getBindValue() { @Override public void setBindValue(Object value, boolean resolveJdbcTypeIfNecessary) { - if ( !handleAsMultiValue( value, null ) ) { + if ( handleAsMultiValue( value, bindType ) ) { + setBindValues( (Collection) value ); + } + else { final Object coerced = coerce( value ); validate( coerced ); if ( value == null ) { @@ -124,46 +131,49 @@ public void setBindValue(Object value, boolean resolveJdbcTypeIfNecessary) { bindNull( resolveJdbcTypeIfNecessary ); } else { - bindValue( coerced ); + initBindType( value ); + bindSingleValue( coerced ); } } } @Override - public void setBindValue(Object value, @Nullable BindableType clarifiedType) { - if ( !handleAsMultiValue( value, clarifiedType ) ) { - if ( clarifiedType != null ) { - bindType = clarifiedType; - } - - final Object coerced = coerce( value ); - validate( coerced ); - bindValue( coerced ); + public void setBindValue( + Object value, + @SuppressWarnings("deprecation") + TemporalType temporalTypePrecision) { + if ( handleAsMultiValue( value, bindType ) ) { + setBindValues( (Collection) value, temporalTypePrecision ); } - } - - @Override - public void setBindValue(Object value, @SuppressWarnings("deprecation") TemporalType temporalTypePrecision) { - if ( !handleAsMultiValue( value, null ) ) { - if ( bindType == null ) { - bindType = queryParameter.getHibernateType(); - } - + else { final Object coerced = coerce( value ); validate( coerced ); - bindValue( coerced ); + initBindType( value ); + bindSingleValue( coerced ); setExplicitTemporalPrecision( temporalTypePrecision ); } } - private void bindValue(Object value) { - if ( canBindValueBeSet( value, bindType ) ) { - bindType = (BindableType) (BindableType) + @Override + public void setBindValue(A value, @Nullable BindableType clarifiedType) { + // don't coerce, because value is already of the clarified type + validate( value ); + clarifyType( value, clarifiedType ); + bindSingleValue( value ); + } + + private void initBindType(Object value) { + if ( bindType == null ) { + @SuppressWarnings("unchecked") + // If there is no bindType set, then this is effectively a + // parameter of the top type. At least arguably safe cast. + final var self = (QueryParameterBindingImpl) this; + //noinspection UnnecessaryLocalVariable (needed to make javac happy) + final var valueType = getParameterBindingTypeResolver() .resolveParameterBindType( value ); + self.bindType = valueType; } - bindValue = cast( value ); - isBound = true; } private void bindNull(boolean resolveJdbcTypeIfNecessary) { @@ -178,18 +188,23 @@ private void bindNull(boolean resolveJdbcTypeIfNecessary) { } } - private boolean handleAsMultiValue(Object value, @Nullable BindableType bindableType) { - if ( queryParameter.allowsMultiValuedBinding() + private boolean handleAsMultiValue(Object value, @Nullable BindableType bindableType) { + return queryParameter.allowsMultiValuedBinding() && value instanceof Collection - && !( bindableType == null + && !validInstance( value, bindableType ); + } + + private void bindSingleValue(Object value) { + bindValue = cast( value ); + bindValues = null; + isMultiValued = false; + isBound = true; + } + + private boolean validInstance(Object value, @Nullable BindableType bindableType) { + return bindableType == null ? isRegisteredAsBasicType( value.getClass() ) - : bindableType.getJavaType().isInstance( value ) ) ) { - setBindValues( (Collection) value ); - return true; - } - else { - return false; - } + : bindableType.getJavaType().isInstance( value ); } private boolean isRegisteredAsBasicType(Class valueClass) { @@ -209,53 +224,47 @@ public Collection getBindValues() { @Override public void setBindValues(Collection values) { - if ( !queryParameter.allowsMultiValuedBinding() ) { - throw new IllegalArgumentException( - "Illegal attempt to bind a collection value to a single-valued parameter" - ); - } - + assertMultivalued(); final var coerced = values.stream().map( this::coerce ).toList(); - values.forEach( this::validate ); - - isBound = true; - isMultiValued = true; - - bindValue = null; - bindValues = coerced.stream().map( this::cast ).toList(); - - final T value = firstNonNull( bindValues ); - if ( canBindValueBeSet( value, bindType ) ) { - bindType = (BindableType) (BindableType) - getParameterBindingTypeResolver() - .resolveParameterBindType( value ); - } + coerced.forEach( this::validate ); + initBindType( firstNonNull( values ) ); + bindMultipleValues( coerced ); } - private static @Nullable T firstNonNull(Collection values) { - final var iterator = values.iterator(); - T value = null; - while ( value == null && iterator.hasNext() ) { - value = iterator.next(); - } - return value; + @Override + public void setBindValues( + Collection values, + @SuppressWarnings("deprecation") TemporalType temporalTypePrecision) { + setBindValues( values ); + setExplicitTemporalPrecision( temporalTypePrecision ); } @Override - public void setBindValues(Collection values, BindableType clarifiedType) { - if ( clarifiedType != null ) { - bindType = clarifiedType; + public void setBindValues(Collection values, BindableType clarifiedType) { + assertMultivalued(); + // don't coerce, because value is already of the clarified type + values.forEach( this::validate ); + clarifyType( values, clarifiedType ); + bindMultipleValues( values ); + } + + private void bindMultipleValues(Collection coerced) { + final List list = new ArrayList<>(); + for ( var value : coerced ) { + list.add( cast( value ) ); } - setBindValues( values ); + bindValues = list; + bindValue = null; + isMultiValued = true; + isBound = true; } - @Override - public void setBindValues( - Collection values, - @SuppressWarnings("deprecation") TemporalType temporalTypePrecision, - TypeConfiguration typeConfiguration) { - setBindValues( values ); - setExplicitTemporalPrecision( temporalTypePrecision ); + private void assertMultivalued() { + if ( !queryParameter.allowsMultiValuedBinding() ) { + throw new IllegalArgumentException( + "Illegal attempt to bind a collection value to a single-valued parameter" + ); + } } private void setExplicitTemporalPrecision(@SuppressWarnings("deprecation") TemporalType precision) { @@ -265,7 +274,7 @@ private void setExplicitTemporalPrecision(@SuppressWarnings("deprecation") Tempo } } - private JavaType determineJavaType(BindableType bindType) { + private JavaType determineJavaType(BindableType bindType) { return getCriteriaBuilder().resolveExpressible( bindType ).getExpressibleJavaType(); } @@ -296,11 +305,55 @@ else if ( type instanceof BasicValuedMapping basicValuedMapping ) { return false; } + private void clarifyType(Object valueOrValues, BindableType clarifiedType) { + if ( clarifiedType != null ) { + checkClarifiedType( clarifiedType, valueOrValues ); + @SuppressWarnings("unchecked") // safe + final var newType = (BindableType) clarifiedType; + bindType = newType; + } + } + + private void checkClarifiedType( + @NonNull BindableType clarifiedType, + Object valueOrValues) { + final var parameterType = queryParameter.getParameterType(); + if ( parameterType != null ) { + final var clarifiedJavaType = clarifiedType.getJavaType(); + if ( !parameterType.isAssignableFrom( clarifiedJavaType ) ) { + throw new QueryArgumentException( + "Given type is incompatible with parameter type", + parameterType, clarifiedJavaType, valueOrValues + ); + } + } + else { + assert queryParameter.getHibernateType() == null; + } + } + private T cast(Object value) { - final var bindableType = getCriteriaBuilder().resolveExpressible( bindType ); - return bindableType == null - ? (T) value // YOLO - : QueryArguments.cast( value, bindableType.getExpressibleJavaType() ); + if ( value == null ) { + return null; + } + else { + final var bindableType = + getCriteriaBuilder() + .resolveExpressible( bindType ); + if ( bindableType != null ) { + return QueryArguments.cast( value, + bindableType.getExpressibleJavaType() ); + } + else if ( bindType != null ) { + return bindType.getJavaType().cast( value ); + } + else { + // no typing information, but in this + // case we can view this as T = Object + // noinspection unchecked + return (T) value; + } + } } private void validate(Object value) { @@ -309,25 +362,19 @@ private void validate(Object value) { private Object coerce(Object value) { try { - if ( canValueBeCoerced( bindType ) ) { + if ( bindType != null ) { return coerce( value, bindType ); } - else if ( canValueBeCoerced( queryParameter.getHibernateType() ) ) { - return coerce( value, queryParameter.getHibernateType() ); - } +// else if ( queryParameter.getHibernateType() != null ) { +// return coerce( value, queryParameter.getHibernateType() ); +// } else { return value; } } catch (HibernateException ex) { - throw new IllegalArgumentException( - String.format( - "Parameter value [%s] did not match expected type [%s]", - value, - bindType - ), - ex - ); + throw new QueryArgumentException( "Argument to query parameter has an incompatible type: " + ex.getMessage(), + queryParameter.getParameterType(), value ); } } @@ -337,11 +384,13 @@ private Object coerce(Object value, BindableType parameterType) { .getExpressibleJavaType().coerce( value ); } - private static boolean canValueBeCoerced(BindableType bindType) { - return bindType != null; + private static @Nullable T firstNonNull(Collection values) { + final var iterator = values.iterator(); + T value = null; + while ( value == null && iterator.hasNext() ) { + value = iterator.next(); + } + return value; } - private static boolean canBindValueBeSet(Object value, BindableType bindType) { - return value != null && bindType == null; - } } 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 a7f63b73fa8e..686c2266e656 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 @@ -65,8 +65,8 @@ private QueryParameterBindingsImpl( ParameterMetadataImplementor parameterMetadata) { this.parameterMetadata = parameterMetadata; final var queryParameters = parameterMetadata.getRegistrations(); - this.parameterBindingMap = linkedMapOfSize( queryParameters.size() ); - this.parameterBindingMapByNameOrPosition = mapOfSize( queryParameters.size() ); + parameterBindingMap = linkedMapOfSize( queryParameters.size() ); + parameterBindingMapByNameOrPosition = mapOfSize( queryParameters.size() ); for ( var queryParameter : queryParameters ) { parameterBindingMap.put( queryParameter, createBinding( sessionFactory, parameterMetadata, queryParameter ) ); @@ -84,28 +84,30 @@ else if ( queryParameter.isOrdinal() ) { } private static QueryParameterBindingImpl createBinding( - SessionFactoryImplementor factory, ParameterMetadataImplementor parameterMetadata, QueryParameter parameter) { + SessionFactoryImplementor factory, + ParameterMetadataImplementor parameterMetadata, + QueryParameter parameter) { return new QueryParameterBindingImpl<>( parameter, factory, parameterMetadata.getInferredParameterType( parameter ) ); } - private QueryParameterBindingsImpl(QueryParameterBindingsImpl original, SessionFactoryImplementor sessionFactory) { + private QueryParameterBindingsImpl( + QueryParameterBindingsImpl original, + SessionFactoryImplementor sessionFactory) { this.parameterMetadata = original.parameterMetadata; this.parameterBindingMap = linkedMapOfSize( original.parameterBindingMap.size() ); - this.parameterBindingMapByNameOrPosition = mapOfSize( original.parameterBindingMapByNameOrPosition.size() ); - for ( var entry : original.parameterBindingMap.entrySet() ) { - parameterBindingMap.put( entry.getKey(), createBinding( sessionFactory, entry.getValue() ) ); - } - for ( var entry : parameterBindingMap.entrySet() ) { - final var queryParameter = entry.getKey(); - final var parameterBinding = entry.getValue(); + this.parameterBindingMapByNameOrPosition = + mapOfSize( original.parameterBindingMapByNameOrPosition.size() ); + original.parameterBindingMap.forEach( (key, value) -> + parameterBindingMap.put( key, createBinding( sessionFactory, value ) ) ); + parameterBindingMap.forEach( (queryParameter, parameterBinding) -> { if ( queryParameter.isNamed() ) { parameterBindingMapByNameOrPosition.put( queryParameter.getName(), parameterBinding ); } - else if ( queryParameter.getPosition() != null ) { + else if ( queryParameter.isOrdinal() ) { parameterBindingMapByNameOrPosition.put( queryParameter.getPosition(), parameterBinding ); } - } + } ); } private static QueryParameterBindingImpl createBinding( @@ -131,7 +133,7 @@ public

QueryParameterBinding

getBinding(QueryParameterImplementor

para ); } if ( !binding.getQueryParameter().equals( parameter ) ) { - throw new IllegalStateException("Parameter binding corrupted for: " + parameter.getName() ); + throw new IllegalStateException( "Parameter binding corrupted for: " + parameter.getName() ); } @SuppressWarnings("unchecked") // safe because we checked the parameter final var castBinding = (QueryParameterBinding

) binding; @@ -164,10 +166,14 @@ public void validate() { if ( !entry.getValue().isBound() ) { final var queryParameter = entry.getKey(); if ( queryParameter.isNamed() ) { - throw new QueryParameterException( "No argument for named parameter ':" + queryParameter.getName() + "'" ); + throw new QueryParameterException( + "No argument for named parameter ':" + + queryParameter.getName() + "'" ); } else { - throw new QueryParameterException( "No argument for ordinal parameter '?" + queryParameter.getPosition() + "'" ); + throw new QueryParameterException( + "No argument for ordinal parameter '?" + + queryParameter.getPosition() + "'" ); } } } @@ -231,7 +237,8 @@ public QueryKey.ParameterBindingsMemento generateQueryKeyMemento(SharedSessionCo private void handleQueryParameters(SharedSessionContractImplementor session, MutableCacheKeyImpl mutableCacheKey) { final var typeConfiguration = session.getFactory().getTypeConfiguration(); - // We know that parameters are consumed in processing order, this ensures consistency of generated cache keys + // We know that parameters are consumed in processing order; + // this ensures the consistency of generated cache keys for ( var entry : parameterBindingMap.entrySet() ) { final var queryParameter = entry.getKey(); final var binding = entry.getValue(); @@ -301,12 +308,14 @@ else if ( binding.getBindValue() != null ) { if ( bindType == null ) { if ( queryParameter.isNamed() ) { - throw new QueryParameterException( "Could not determine mapping type for named parameter ':" - + queryParameter.getName() + "'" ); + throw new QueryParameterException( + "Could not determine mapping type for named parameter ':" + + queryParameter.getName() + "'" ); } else { - throw new QueryParameterException( "Could not determine mapping type for ordinal parameter '?" - + queryParameter.getPosition() + "'" ); + throw new QueryParameterException( + "Could not determine mapping type for ordinal parameter '?" + + queryParameter.getPosition() + "'" ); } } diff --git a/hibernate-core/src/main/java/org/hibernate/query/spi/AbstractCommonQueryContract.java b/hibernate-core/src/main/java/org/hibernate/query/spi/AbstractCommonQueryContract.java index 4817faa4374a..8fa86412f56e 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/spi/AbstractCommonQueryContract.java +++ b/hibernate-core/src/main/java/org/hibernate/query/spi/AbstractCommonQueryContract.java @@ -11,6 +11,7 @@ import jakarta.persistence.Parameter; import jakarta.persistence.TemporalType; import jakarta.persistence.metamodel.Type; +import org.checkerframework.checker.nullness.qual.NonNull; import org.hibernate.FlushMode; import org.hibernate.HibernateException; import org.hibernate.Internal; @@ -144,14 +145,16 @@ protected int getIntegerLiteral(JpaExpression expression, int defaultVal if ( expression == null ) { return defaultValue; } - else if ( expression instanceof SqmLiteral ) { - return ( (SqmLiteral) expression ).getLiteralValue().intValue(); + else if ( expression instanceof SqmLiteral numericLiteral ) { + return numericLiteral.getLiteralValue().intValue(); } - else if ( expression instanceof Parameter ) { - final Number parameterValue = getParameterValue( (Parameter) expression ); - return parameterValue == null ? defaultValue : parameterValue.intValue(); + else if ( expression instanceof SqmParameter parameterExpression ) { + final Number number = getParameterValue( parameterExpression ); + return number == null ? defaultValue : number.intValue(); + } + else { + throw new IllegalArgumentException( "Not an integer literal: " + expression ); } - throw new IllegalArgumentException( "Can't get integer literal value from: " + expression ); } protected int getMaxRows(SqmSelectStatement selectStatement, int size) { @@ -171,11 +174,11 @@ protected int getMaxRows(SqmSelectStatement selectStatement, int size) { } private Number fetchValue(JpaExpression expression) { - if ( expression instanceof SqmLiteral ) { - return ((SqmLiteral) expression).getLiteralValue(); + if ( expression instanceof SqmLiteral numericLiteral ) { + return numericLiteral.getLiteralValue(); } - else if ( expression instanceof SqmParameter ) { - return getParameterValue( (Parameter) expression ); + else if ( expression instanceof SqmParameter numericParameter ) { + return getParameterValue( numericParameter ); } else { throw new IllegalArgumentException( "Can't get max rows value from: " + expression ); @@ -695,6 +698,7 @@ public QueryParameterImplementor getParameter(int position, Class type @Internal // Made public to work around this bug: https://bugs.openjdk.org/browse/JDK-8340443 public abstract QueryParameterBindings getQueryParameterBindings(); + protected abstract boolean resolveJdbcParameterTypeIfNecessary(); private

JavaType

getJavaType(Class

javaType) { @@ -702,124 +706,47 @@ private

JavaType

getJavaType(Class

javaType) { .resolveDescriptor( javaType ); } - protected

QueryParameterBinding

locateBinding(Parameter

parameter, P value) { - if ( parameter instanceof QueryParameterImplementor

parameterImplementor ) { - return locateBinding( parameterImplementor ); + protected QueryParameterBinding locateBinding(Parameter parameter) { + if ( parameter instanceof QueryParameterImplementor parameterImplementor ) { + getCheckOpen(); + return getQueryParameterBindings().getBinding( getQueryParameter( parameterImplementor ) ); } else { - final String name = parameter.getName(); - final Integer position = parameter.getPosition(); - final var parameterType = parameter.getParameterType(); - if ( name != null ) { - return locateBinding( name, parameterType, value ); - } - else if ( position != null ) { - return locateBinding( position, parameterType, value ); - } + return locateBinding( parameter.getName(), parameter.getPosition() ); } - - throw getExceptionConverter().convert( - new IllegalArgumentException( "Could not resolve binding for given parameter reference [" + parameter + "]" ) - ); } - protected

QueryParameterBinding

locateBinding(Parameter

parameter, Collection values) { - if ( parameter instanceof QueryParameterImplementor

parameterImplementor ) { - return locateBinding( parameterImplementor ); - } - else { - final String name = parameter.getName(); - final Integer position = parameter.getPosition(); - final var parameterType = parameter.getParameterType(); - if ( name != null ) { - return locateBinding( name, parameterType, values ); - } - else if ( position != null ) { - return locateBinding( position, parameterType, values ); + private @NonNull QueryParameterBinding locateBinding(String name, Integer position) { + final var bindings = getQueryParameterBindings(); + if ( name != null ) { + final var binding = bindings.getBinding( name ); + if ( binding == null ) { + // should never occur + throw new IllegalArgumentException( "No binding for given parameter named '" + name + "'" ); } + return binding; } - - throw getExceptionConverter().convert( - new IllegalArgumentException( "Could not resolve binding for given parameter reference [" + parameter + "]" ) - ); - } - - protected

QueryParameterBinding

locateBinding(QueryParameterImplementor

parameter) { - getCheckOpen(); - return getQueryParameterBindings() - .getBinding( getQueryParameter( parameter ) ); - } - - protected

QueryParameterBinding

locateBinding(String name, Class

javaType, P value) { - getCheckOpen(); - final var binding = getQueryParameterBindings().getBinding( name ); - final var parameterType = binding.getBindType(); - if ( parameterType != null ) { - final var parameterJavaType = parameterType.getJavaType(); - if ( !parameterJavaType.isAssignableFrom( javaType ) - && !isInstance( parameterType, value, getNodeBuilder() ) ) { - throw new QueryArgumentException( - "Argument to parameter named '" + name + "' has an incompatible type", - parameterJavaType, javaType, value ); + else if ( position != null ) { + final var binding = bindings.getBinding( position ); + if ( binding == null ) { + // should never occur + throw new IllegalArgumentException( "No binding for given parameter at position " + position ); } + return binding; } - @SuppressWarnings("unchecked") // safe, just checked - var castBinding = (QueryParameterBinding

) binding; - return castBinding; - } - - protected

QueryParameterBinding

locateBinding(int position, Class

javaType, P value) { - getCheckOpen(); - final var binding = getQueryParameterBindings().getBinding( position ); - final var parameterType = binding.getBindType(); - if ( parameterType != null ) { - final var parameterJavaType = parameterType.getJavaType(); - if ( !parameterJavaType.isAssignableFrom( javaType ) - && !isInstance( parameterType, value, getNodeBuilder() ) ) { - throw new QueryArgumentException( - "Argument to parameter at position " + position + " has an incompatible type", - parameterJavaType, javaType, value ); - } + else { + throw new IllegalArgumentException( "Parameter must have either a name or a position" ); } - @SuppressWarnings("unchecked") // safe, just checked - var castBinding = (QueryParameterBinding

) binding; - return castBinding; } - protected

QueryParameterBinding

locateBinding(String name, Class

javaType, Collection values) { + protected QueryParameterBinding locateBinding(String name) { getCheckOpen(); - final var binding = getQueryParameterBindings().getBinding( name ); - final var parameterType = binding.getBindType(); - if ( parameterType != null ) { - final var parameterJavaType = parameterType.getJavaType(); - if ( !parameterJavaType.isAssignableFrom( javaType ) - && !areInstances( parameterType, values, getNodeBuilder() ) ) { - throw new QueryArgumentException( - "Argument to parameter named '" + name + "' has an incompatible type", - parameterJavaType, javaType, values ); - } - } - @SuppressWarnings("unchecked") // safe, just checked - var castBinding = (QueryParameterBinding

) binding; - return castBinding; + return getQueryParameterBindings().getBinding( name ); } - protected

QueryParameterBinding

locateBinding(int position, Class

javaType, Collection values) { + protected QueryParameterBinding locateBinding(int position) { getCheckOpen(); - final var binding = getQueryParameterBindings().getBinding( position ); - final var parameterType = binding.getBindType(); - if ( parameterType != null ) { - final var parameterJavaType = parameterType.getJavaType(); - if ( !parameterJavaType.isAssignableFrom( javaType ) - && !areInstances( parameterType, values, getNodeBuilder() ) ) { - throw new QueryArgumentException( - "Argument to parameter at position " + position + " has an incompatible type", - parameterJavaType, javaType, values ); - } - } - @SuppressWarnings("unchecked") // safe, just checked - var castBinding = (QueryParameterBinding

) binding; - return castBinding; + return getQueryParameterBindings().getBinding( position ); } protected

QueryParameterImplementor

getQueryParameter(QueryParameterImplementor

parameter) { @@ -843,10 +770,10 @@ public T getParameterValue(Parameter param) { getQueryParameterBindings() .getBinding( getQueryParameter( parameter ) ); if ( binding == null || !binding.isBound() ) { - throw new IllegalStateException( "Parameter value not yet bound : " + param.toString() ); + throw new IllegalStateException( "Parameter value not yet bound : " + param ); } if ( binding.isMultiValued() ) { - // TODO: THIS IS UNSOUND + // TODO: THIS IS UNSOUND, we should really throw in this case //noinspection unchecked return (T) binding.getBindValues(); } @@ -859,7 +786,7 @@ public Object getParameterValue(String name) { checkOpenNoRollback(); final var binding = getQueryParameterBindings().getBinding( name ); if ( !binding.isBound() ) { - throw new IllegalStateException( "The parameter [" + name + "] has not yet been bound" ); + throw new IllegalStateException( "The parameter named '" + name + "' has no argument" ); } return binding.isMultiValued() ? binding.getBindValues() : binding.getBindValue(); } @@ -868,30 +795,11 @@ public Object getParameterValue(int position) { checkOpenNoRollback(); final var binding = getQueryParameterBindings().getBinding( position ); if ( !binding.isBound() ) { - throw new IllegalStateException( "The parameter [" + position + "] has not yet been bound" ); + throw new IllegalStateException( "The parameter at position" + position + " has no argument" ); } return binding.isMultiValued() ? binding.getBindValues() : binding.getBindValue(); } - private

void setParameterValue(Object value, QueryParameterBinding

binding) { - final var parameterType = binding.getBindType(); - if ( parameterType != null - && !isInstanceOrAreInstances( value, binding, parameterType ) ) { - throw new QueryArgumentException( "Argument to query parameter has an incompatible type", - parameterType.getJavaType(), value.getClass(), value ); - } - @SuppressWarnings("unchecked") // safe, just checked - final var castValue = (P) value; - binding.setBindValue( castValue, resolveJdbcParameterTypeIfNecessary() ); - } - - private

boolean isInstanceOrAreInstances( - Object value, QueryParameterBinding

binding, BindableType parameterType) { - return binding.isMultiValued() && value instanceof Collection values - ? areInstances( parameterType, values, getNodeBuilder() ) - : isInstance( parameterType, value, getNodeBuilder() ); - } - @Override public CommonQueryContract setParameter(String name, Object value) { checkOpenNoRollback(); @@ -904,7 +812,7 @@ public CommonQueryContract setParameter(String name, Object value) { setParameterList( name, (Collection) value ); } else { - setParameterValue( value, binding ); + binding.setBindValue( value, resolveJdbcParameterTypeIfNecessary() ); } } return this; @@ -952,13 +860,13 @@ public

CommonQueryContract setParameter(String name, P value, Class

javaT @Override public

CommonQueryContract setParameter(String name, P value, Type

type) { - locateBinding( name, type.getJavaType(), value ).setBindValue( value, (BindableType

) type ); + locateBinding( name ).setBindValue( value, (BindableType

) type ); return this; } @Override @Deprecated(since = "7") public CommonQueryContract setParameter(String name, Instant value, TemporalType temporalType) { - locateBinding( name, Instant.class, value ).setBindValue( value, temporalType ); + locateBinding( name ).setBindValue( value, temporalType ); return this; } @@ -974,7 +882,7 @@ public CommonQueryContract setParameter(int position, Object value) { setParameterList( position, (Collection) value ); } else { - setParameterValue( value, binding ); + binding.setBindValue( value, resolveJdbcParameterTypeIfNecessary() ); } } return this; @@ -998,20 +906,19 @@ public

CommonQueryContract setParameter(int position, P value, Class

java @Override public

CommonQueryContract setParameter(int position, P value, Type

type) { - locateBinding( position, type.getJavaType(), value ).setBindValue( value, (BindableType

) type ); + locateBinding( position ).setBindValue( value, (BindableType

) type ); return this; } @Override @Deprecated(since = "7") public CommonQueryContract setParameter(int position, Instant value, TemporalType temporalType) { - locateBinding( position, Instant.class, value ).setBindValue( value, temporalType ); + locateBinding( position ).setBindValue( value, temporalType ); return this; } @Override public

CommonQueryContract setParameter(QueryParameter

parameter, P value) { - locateBinding( parameter, value ) - .setBindValue( value, resolveJdbcParameterTypeIfNecessary() ); + locateBinding( parameter ).setBindValue( value, resolveJdbcParameterTypeIfNecessary() ); return this; } @@ -1029,8 +936,7 @@ public

CommonQueryContract setParameter(QueryParameter

parameter, P value @Override public

CommonQueryContract setParameter(QueryParameter

parameter, P value, Type

type) { - locateBinding( parameter, value ) - .setBindValue( value, (BindableType

) type ); + locateBinding( parameter ).setBindValue( value, (BindableType

) type ); return this; } @@ -1055,50 +961,48 @@ public

CommonQueryContract setParameter(Parameter

parameter, P value) { setParameter( queryParameter, castValue, castType ); } else { - locateBinding( parameter, castValue ) - .setBindValue( castValue, castType ); + locateBinding( parameter ).setBindValue( castValue, castType ); } } else { - locateBinding( parameter, value ) - .setBindValue( value, resolveJdbcParameterTypeIfNecessary() ); + locateBinding( parameter ).setBindValue( value, resolveJdbcParameterTypeIfNecessary() ); } return this; } @Override @Deprecated public CommonQueryContract setParameter(Parameter param, Calendar value, TemporalType temporalType) { - locateBinding( param, value ).setBindValue( value, temporalType ); + locateBinding( param ).setBindValue( value, temporalType ); return this; } @Override @Deprecated public CommonQueryContract setParameter(Parameter param, Date value, TemporalType temporalType) { - locateBinding( param, value ).setBindValue( value, temporalType ); + locateBinding( param ).setBindValue( value, temporalType ); return this; } @Override @Deprecated public CommonQueryContract setParameter(String name, Calendar value, TemporalType temporalType) { - locateBinding( name, Calendar.class, value ).setBindValue( value, temporalType ); + locateBinding( name ).setBindValue( value, temporalType ); return this; } @Override @Deprecated public CommonQueryContract setParameter(String name, Date value, TemporalType temporalType) { - locateBinding( name, Date.class, value ).setBindValue( value, temporalType ); + locateBinding( name ).setBindValue( value, temporalType ); return this; } @Override @Deprecated public CommonQueryContract setParameter(int position, Calendar value, TemporalType temporalType) { - locateBinding( position, Calendar.class, value ).setBindValue( value, temporalType ); + locateBinding( position ).setBindValue( value, temporalType ); return this; } @Override @Deprecated public CommonQueryContract setParameter(int position, Date value, TemporalType temporalType) { - locateBinding( position, Date.class, value ).setBindValue( value, temporalType ); + locateBinding( position ).setBindValue( value, temporalType ); return this; } @@ -1119,11 +1023,9 @@ public

CommonQueryContract setParameterList(String name, Collection CommonQueryContract setParameterList(String name, Collection values, Type

type) { - locateBinding( name, type.getJavaType(), values ) - .setBindValues( values, (BindableType

) type ); + locateBinding( name ).setBindValues( values, (BindableType

) type ); return this; } @@ -1159,9 +1061,7 @@ public

CommonQueryContract setParameterList(String name, P[] values, Class

CommonQueryContract setParameterList(String name, P[] values, Type

type) { - final var list = List.of( values ); - locateBinding( name, type.getJavaType(), list ) - .setBindValues( list, (BindableType

) type ); + locateBinding( name ).setBindValues( List.of( values ), (BindableType

) type ); return this; } @@ -1206,8 +1106,7 @@ private

Type

getParamType(Class

javaType) { @Override public

CommonQueryContract setParameterList(int position, Collection values, Type

type) { - locateBinding( position, type.getJavaType(), values ) - .setBindValues( values, (BindableType

) type ); + locateBinding( position ).setBindValues( values, (BindableType

) type ); return this; } @@ -1231,16 +1130,14 @@ public

CommonQueryContract setParameterList(int position, P[] values, Class< } public

CommonQueryContract setParameterList(int position, P[] values, Type

type) { - final var list = List.of( values ); - locateBinding( position, type.getJavaType(), list ) - .setBindValues( list, (BindableType

) type ); + locateBinding( position ).setBindValues( List.of( values ), (BindableType

) type ); return this; } @Override public

CommonQueryContract setParameterList(QueryParameter

parameter, Collection values) { - locateBinding( parameter, values ).setBindValues( values ); + locateBinding( parameter ).setBindValues( values ); return this; } @@ -1258,15 +1155,13 @@ public

CommonQueryContract setParameterList(QueryParameter

parameter, Col @Override public

CommonQueryContract setParameterList(QueryParameter

parameter, Collection values, Type

type) { - locateBinding( parameter, values ) - .setBindValues( values, (BindableType

) type ); + locateBinding( parameter ).setBindValues( values, (BindableType

) type ); return this; } @Override public

CommonQueryContract setParameterList(QueryParameter

parameter, P[] values) { - final var list = List.of( values ); - locateBinding( parameter, list ).setBindValues( list ); + locateBinding( parameter ).setBindValues( List.of( values ) ); return this; } @@ -1284,9 +1179,7 @@ public

CommonQueryContract setParameterList(QueryParameter

parameter, P[] @Override public

CommonQueryContract setParameterList(QueryParameter

parameter, P[] values, Type

type) { - final var list = List.of( values ); - locateBinding( parameter, list ) - .setBindValues( list, (BindableType

) type ); + locateBinding( parameter ).setBindValues( List.of( values ), (BindableType

) type ); return this; } diff --git a/hibernate-core/src/main/java/org/hibernate/query/spi/QueryParameterBinding.java b/hibernate-core/src/main/java/org/hibernate/query/spi/QueryParameterBinding.java index bcef863253b0..0b1b0a6a2c67 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/spi/QueryParameterBinding.java +++ b/hibernate-core/src/main/java/org/hibernate/query/spi/QueryParameterBinding.java @@ -10,21 +10,35 @@ import org.checkerframework.checker.nullness.qual.Nullable; import org.hibernate.Incubating; import org.hibernate.metamodel.mapping.MappingModelExpressible; +import org.hibernate.query.QueryArgumentException; import org.hibernate.query.QueryParameter; import org.hibernate.type.BindableType; -import org.hibernate.type.spi.TypeConfiguration; /** - * The value/type binding information for a particular query parameter. Supports - * both single-valued and multivalued binds + * The value and type binding information for a particular query parameter. + * Can represent both single-valued and multivalued bindings of arguments + * to a parameter. + *

+ * The operations of this interface attempt to assign argument values to + * the parameter of type {@code T}. If the argument cannot be coerced to + * {@code T}, the operation fails and throws {@link QueryArgumentException}. + * + * @param The type of the query parameter * * @author Steve Ebersole */ @Incubating public interface QueryParameterBinding { /** - * Is any value (including {@code null}) bound? Asked another way, - * were any of the `#set` methods called? + * The query parameter associated with this binding. + */ + QueryParameter getQueryParameter(); + + /** + * Is any argument (even a {@code null} argument) currently + * bound to the parameter? That is, was one of the + * {@link #setBindValue} or {@link #setBindValues} methods + * execute successfully? */ boolean isBound(); @@ -33,92 +47,129 @@ public interface QueryParameterBinding { */ boolean isMultiValued(); - QueryParameter getQueryParameter(); - /** - * Get the Type currently associated with this binding. + * Get the type currently associated with this binding. + * By default, this is the type inferred from the parameter. + * It may be modified via a call to {@link #setBindValue} or + * {@link #setBindValues}. * - * @return The currently associated Type + * @return The currently associated {@link BindableType} */ @Nullable BindableType getBindType(); /** - * If the parameter represents a temporal type, return the explicitly - * specified precision - if one. + * If the parameter is of a temporal type, return the explicitly + * specified precision, if any. */ @Nullable @SuppressWarnings("deprecation") TemporalType getExplicitTemporalPrecision(); /** - * Sets the parameter binding value. The inherent parameter type (if known) is assumed + * Set argument. If the given value is a {@link Collection}, + * it might be interpreted as multiple arguments. Use the + * inherent type of the parameter. + * + * @throws QueryArgumentException + * if the value cannot be bound to the parameter */ default void setBindValue(Object value) { setBindValue( value, false ); } /** - * Sets the parameter binding value. The inherent parameter type (if known) is assumed. - * The flag controls whether the parameter type should be resolved if necessary. + * Set argument. If the given value is a {@link Collection}, + * it might be interpreted as multiple arguments. Use the + * inherent type of the parameter. + * + * @param resolveJdbcTypeIfNecessary + * Controls whether the parameter type should be + * resolved if necessary. + * @throws QueryArgumentException + * if the value cannot be bound to the parameter */ void setBindValue(Object value, boolean resolveJdbcTypeIfNecessary); /** - * Sets the parameter binding value using the explicit Type. - * @param value The bind value - * @param clarifiedType The explicit Type to use + * Set the argument, specifying an explicit {@link BindableType}. + * + * @param value The argument + * @param clarifiedType The explicit type */ - void setBindValue(Object value, @Nullable BindableType clarifiedType); + void setBindValue(A value, @Nullable BindableType clarifiedType); /** - * Sets the parameter binding value using the explicit TemporalType. - * @param value The bind value - * @param temporalTypePrecision The temporal type to use + * Set the argument, specifying an explicit {@link TemporalType}. + * If the given value is a {@link Collection}, it might be interpreted + * as multiple arguments. + * + * @param value The argument + * @param temporalTypePrecision The explicit temporal type + * @throws QueryArgumentException + * if the value cannot be bound to the parameter */ void setBindValue(Object value, @SuppressWarnings("deprecation") TemporalType temporalTypePrecision); /** - * Get the value current bound. + * Get the argument currently bound to the parameter. * - * @return The currently bound value + * @return The argument currently bound + * @throws IllegalStateException + * if the parameter is multivalued */ T getBindValue(); /** - * Sets the parameter binding values. The inherent parameter type (if known) is assumed in regards to the - * individual values. - * @param values The bind values + * Attempt to set multiple arguments to the parameter. Use the + * inherent type of the parameter. * + * @param values The arguments + * @throws IllegalArgumentException if the parameter is not multivalued + * @throws QueryArgumentException + * if one of the values cannot be bound to the parameter */ void setBindValues(Collection values); /** - * Sets the parameter binding values using the explicit Type in regards to the individual values. - * @param values The bind values - * @param clarifiedType The explicit Type to use + * Attempt to set multiple arguments to the parameter, specifying + * an explicit {@link BindableType}. + * + * @param values The arguments + * @param clarifiedType The explicit type + * @throws IllegalArgumentException + * if the parameter is not multivalued + * @throws QueryArgumentException + * if one of the values cannot be bound to the parameter */ - void setBindValues(Collection values, BindableType clarifiedType); + void setBindValues(Collection values, BindableType clarifiedType); - /**Sets the parameter binding value using the explicit TemporalType in regards to the individual values. + /** + * Attempt to set multiple arguments to the parameter, specifying + * an explicit {@link TemporalType}. * - * @param values The bind values - * @param temporalTypePrecision The temporal type to use + * @param values The arguments + * @param temporalTypePrecision The explicit temporal type + * @throws IllegalArgumentException + * if the parameter is not multivalued + * @throws QueryArgumentException + * if one of the values cannot be bound to the parameter */ void setBindValues( Collection values, @SuppressWarnings("deprecation") - TemporalType temporalTypePrecision, - TypeConfiguration typeConfiguration); + TemporalType temporalTypePrecision); /** - * Get the values currently bound. + * Get the arguments currently bound to the parameter. * - * @return The currently bound values + * @return The arguments currently bound + * @throws IllegalArgumentException if the parameter is not multivalued */ Collection getBindValues(); /** - * Returns the inferred mapping model expressible i.e. the model reference against which this parameter is compared. + * Returns the inferred mapping model expressible, i.e., + * the model reference against which this parameter is compared. * - * @return the inferred mapping model expressible or null + * @return the inferred mapping model expressible or {@code null} */ @Nullable MappingModelExpressible getType(); @@ -126,7 +177,7 @@ void setBindValues( * Sets the mapping model expressible for this parameter. * * @param type The mapping model expressible - * @return Whether the bind type was changed + * @return Whether the binding type was actually changed */ boolean setType(@Nullable MappingModelExpressible type); } diff --git a/hibernate-core/src/main/java/org/hibernate/query/spi/QueryParameterBindings.java b/hibernate-core/src/main/java/org/hibernate/query/spi/QueryParameterBindings.java index faffc2c44f27..be32890f6251 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/spi/QueryParameterBindings.java +++ b/hibernate-core/src/main/java/org/hibernate/query/spi/QueryParameterBindings.java @@ -94,7 +94,6 @@ default

QueryParameterBinding

getBinding(QueryParameter

parameter) { * Currently unused and can be safely removed. */ @Deprecated(forRemoval = true, since = "6.6") -// @SuppressWarnings({"rawtypes", "unchecked"}) QueryParameterBindings NO_PARAM_BINDINGS = new QueryParameterBindings() { @Override public boolean isBound(QueryParameterImplementor parameter) { diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/AbstractSqmSelectionQuery.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/AbstractSqmSelectionQuery.java index 520cbf830274..1a9bfa01965d 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/AbstractSqmSelectionQuery.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/AbstractSqmSelectionQuery.java @@ -9,8 +9,8 @@ import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.query.spi.QueryParameterImplementor; import org.hibernate.query.sqm.tree.expression.JpaCriteriaParameter; +import org.hibernate.query.sqm.tree.expression.SqmParameter; import org.hibernate.query.sqm.tree.expression.ValueBindJpaCriteriaParameter; -import org.hibernate.type.BindableType; import org.hibernate.query.KeyedPage; import org.hibernate.query.KeyedResultList; import org.hibernate.query.Page; @@ -20,7 +20,6 @@ import org.hibernate.query.spi.HqlInterpretation; import org.hibernate.query.spi.MutableQueryOptions; import org.hibernate.query.spi.QueryOptions; -import org.hibernate.query.spi.QueryParameterBinding; import org.hibernate.query.spi.QueryParameterBindings; import org.hibernate.query.spi.SelectQueryPlan; import org.hibernate.query.sqm.spi.NamedSqmQueryMemento; @@ -98,65 +97,68 @@ protected void errorOrLogForPaginationWithCollectionFetch() { } public abstract SqmStatement getSqmStatement(); - protected abstract void setSqmStatement(SqmSelectStatement statement); +// protected abstract void setSqmStatement(SqmSelectStatement statement); public abstract DomainParameterXref getDomainParameterXref(); public abstract TupleMetadata getTupleMetadata(); - - protected void copyParameterBindings(QueryParameterBindings oldParameterBindings) { - final var parameterBindings = getQueryParameterBindings(); - oldParameterBindings.visitBindings( (queryParameter, binding) -> { - if ( binding.isBound() ) { - //noinspection unchecked - final var newBinding = (QueryParameterBinding) parameterBindings.getBinding( queryParameter ); - //noinspection unchecked - final var bindType = (BindableType) binding.getBindType(); - final var explicitTemporalPrecision = binding.getExplicitTemporalPrecision(); - if ( binding.isMultiValued() ) { - final var bindValues = binding.getBindValues(); - if ( explicitTemporalPrecision != null ) { - newBinding.setBindValues( bindValues, explicitTemporalPrecision, getTypeConfiguration() ); - } - else if ( bindType != null ) { - newBinding.setBindValues( bindValues, bindType ); - } - else { - newBinding.setBindValues( bindValues ); - } - } - else { - final var bindValue = binding.getBindValue(); - if ( explicitTemporalPrecision != null ) { - newBinding.setBindValue( bindValue, explicitTemporalPrecision ); - } - else if ( bindType != null ) { - newBinding.setBindValue( bindValue, bindType ); - } - else { - newBinding.setBindValue( bindValue ); - } - } - } - } ); - - // Parameters might be created through HibernateCriteriaBuilder.value which we need to bind here - bindValueBindCriteriaParameters( getDomainParameterXref(), parameterBindings ); - } +// +// protected void copyParameterBindings(QueryParameterBindings oldParameterBindings) { +// final var parameterBindings = getQueryParameterBindings(); +// oldParameterBindings.visitBindings( (queryParameter, binding) -> { +// if ( binding.isBound() ) { +// final var newBinding = parameterBindings.getBinding( queryParameter ); +// final var bindType = (BindableType) binding.getBindType(); +// final var explicitTemporalPrecision = binding.getExplicitTemporalPrecision(); +// if ( binding.isMultiValued() ) { +// final var bindValues = binding.getBindValues(); +// if ( explicitTemporalPrecision != null ) { +// newBinding.setBindValues( bindValues, explicitTemporalPrecision, getTypeConfiguration() ); +// } +// else if ( bindType != null ) { +// newBinding.setBindValues( bindValues, bindType ); +// } +// else { +// newBinding.setBindValues( bindValues ); +// } +// } +// else { +// final var bindValue = binding.getBindValue(); +// if ( explicitTemporalPrecision != null ) { +// newBinding.setBindValue( bindValue, explicitTemporalPrecision ); +// } +// else if ( bindType != null ) { +// newBinding.setBindValue( bindValue, bindType ); +// } +// else { +// newBinding.setBindValue( bindValue ); +// } +// } +// } +// } ); +// +// // Parameters might be created through HibernateCriteriaBuilder.value which we need to bind here +// bindValueBindCriteriaParameters( getDomainParameterXref(), parameterBindings ); +// } protected static void bindValueBindCriteriaParameters( DomainParameterXref domainParameterXref, QueryParameterBindings bindings) { for ( var entry : domainParameterXref.getQueryParameters().entrySet() ) { - final var sqmParameter = entry.getValue().get( 0 ); - if ( sqmParameter instanceof SqmJpaCriteriaParameterWrapper wrapper ) { - @SuppressWarnings("unchecked") - final var criteriaParameter = (JpaCriteriaParameter) wrapper.getJpaCriteriaParameter(); - if ( criteriaParameter instanceof ValueBindJpaCriteriaParameter ) { - // Use the anticipated type for binding the value if possible - //noinspection unchecked - final var parameter = (QueryParameterImplementor) entry.getKey(); - bindings.getBinding( parameter ) - .setBindValue( criteriaParameter.getValue(), criteriaParameter.getAnticipatedType() ); - } + bindValueToCriteriaParameter( bindings, entry.getKey(), + entry.getValue().get( 0 ) ); + } + } + + private static void bindValueToCriteriaParameter( + QueryParameterBindings bindings, + QueryParameterImplementor queryParameterImplementor, + SqmParameter sqmParameter) { + if ( sqmParameter instanceof SqmJpaCriteriaParameterWrapper wrapper ) { + final var criteriaParameter = wrapper.getJpaCriteriaParameter(); + if ( criteriaParameter instanceof ValueBindJpaCriteriaParameter ) { + // Use the anticipated type for binding the value if possible + bindings.getBinding( queryParameterImplementor ) + .setBindValue( criteriaParameter.getValue(), + criteriaParameter.getAnticipatedType() ); } } } @@ -164,11 +166,14 @@ protected static void bindValueBindCriteriaParameters( @Override protected

QueryParameterImplementor

getQueryParameter(QueryParameterImplementor

parameter) { if ( parameter instanceof JpaCriteriaParameter criteriaParameter ) { - final var parameterWrapper = getDomainParameterXref().getParameterResolutions() - .getJpaCriteriaParamResolutions() - .get( criteriaParameter ); + final var parameterWrapper = + getDomainParameterXref().getParameterResolutions() + .getJpaCriteriaParamResolutions() + .get( criteriaParameter ); //noinspection unchecked - return (QueryParameterImplementor

) getDomainParameterXref().getQueryParameter( parameterWrapper ); + return (QueryParameterImplementor

) + getDomainParameterXref() + .getQueryParameter( parameterWrapper ); } else { return parameter; @@ -176,9 +181,9 @@ protected

QueryParameterImplementor

getQueryParameter(QueryParameterImple } public int @Nullable [] unnamedParameterIndices() { - final var domainParameterXref = getDomainParameterXref(); final var jpaCriteriaParamResolutions = - domainParameterXref.getParameterResolutions().getJpaCriteriaParamResolutions(); + getDomainParameterXref().getParameterResolutions() + .getJpaCriteriaParamResolutions(); if ( jpaCriteriaParamResolutions.isEmpty() ) { return null; } @@ -188,7 +193,8 @@ protected

QueryParameterImplementor

getQueryParameter(QueryParameterImple } final var unnamedParameterIndices = new int[maxId + 1]; for ( var entry : jpaCriteriaParamResolutions.entrySet() ) { - unnamedParameterIndices[entry.getValue().getCriteriaParameterId()] = entry.getValue().getUnnamedParameterId(); + unnamedParameterIndices[entry.getValue().getCriteriaParameterId()] = + entry.getValue().getUnnamedParameterId(); } return unnamedParameterIndices; } @@ -353,12 +359,14 @@ private TupleMetadata getTupleMetadata(List> selections) { } private static TupleElement[] buildTupleElementArray(List> selections) { - if ( selections.size() == 1 ) { + final int selectionsSize = selections.size(); + if ( selectionsSize == 1 ) { final var selectableNode = selections.get( 0 ).getSelectableNode(); if ( selectableNode instanceof CompoundSelection ) { final var selectionItems = selectableNode.getSelectionItems(); - final var elements = new TupleElement[ selectionItems.size() ]; - for ( int i = 0; i < selectionItems.size(); i++ ) { + final int itemsSize = selectionItems.size(); + final var elements = new TupleElement[itemsSize]; + for ( int i = 0; i < itemsSize; i++ ) { elements[i] = selectionItems.get( i ); } return elements; @@ -368,8 +376,8 @@ private static TupleElement[] buildTupleElementArray(List> se } } else { - final var elements = new TupleElement[ selections.size() ]; - for ( int i = 0; i < selections.size(); i++ ) { + final var elements = new TupleElement[selectionsSize]; + for ( int i = 0; i < selectionsSize; i++ ) { elements[i] = selections.get( i ).getSelectableNode(); } return elements; @@ -377,12 +385,14 @@ private static TupleElement[] buildTupleElementArray(List> se } private static String[] buildTupleAliasArray(List> selections) { - if ( selections.size() == 1 ) { + final int selectionsSize = selections.size(); + if ( selectionsSize == 1 ) { final var selectableNode = selections.get(0).getSelectableNode(); if ( selectableNode instanceof CompoundSelection ) { final var selectionItems = selectableNode.getSelectionItems(); - final String[] elements = new String[ selectionItems.size() ]; - for ( int i = 0; i < selectionItems.size(); i++ ) { + final int itemsSize = selectionItems.size(); + final var elements = new String[itemsSize]; + for ( int i = 0; i < itemsSize; i++ ) { elements[i] = selectionItems.get( i ).getAlias(); } return elements; @@ -392,8 +402,8 @@ private static String[] buildTupleAliasArray(List> selections) { } } else { - final String[] elements = new String[ selections.size() ]; - for ( int i = 0; i < selections.size(); i++ ) { + final String[] elements = new String[selectionsSize]; + for ( int i = 0; i < selectionsSize; i++ ) { elements[i] = selections.get( i ).getAlias(); } return elements; diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/DomainParameterXref.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/DomainParameterXref.java index fb8b74af46b8..b9d9cbd5b22c 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/DomainParameterXref.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/DomainParameterXref.java @@ -4,12 +4,7 @@ */ package org.hibernate.query.sqm.internal; -import java.util.ArrayList; -import java.util.IdentityHashMap; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; - +import org.checkerframework.checker.nullness.qual.NonNull; import org.hibernate.query.internal.QueryParameterIdentifiedImpl; import org.hibernate.query.internal.QueryParameterNamedImpl; import org.hibernate.query.internal.QueryParameterPositionalImpl; @@ -20,6 +15,13 @@ import org.hibernate.query.sqm.tree.expression.SqmParameter; import org.hibernate.type.BasicCollectionType; +import java.util.ArrayList; +import java.util.IdentityHashMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + import static java.util.Collections.emptyList; /** @@ -29,67 +31,19 @@ */ public class DomainParameterXref { - public static final DomainParameterXref EMPTY = new DomainParameterXref( - new LinkedHashMap<>( 0 ), - new IdentityHashMap<>( 0 ), - SqmStatement.ParameterResolutions.empty() - ); + public static final DomainParameterXref EMPTY = new DomainParameterXref(); /** * Create a DomainParameterXref for the parameters defined in the SQM statement */ public static DomainParameterXref from(SqmStatement sqmStatement) { - final SqmStatement.ParameterResolutions parameterResolutions = sqmStatement.resolveParameters(); - if ( parameterResolutions.getSqmParameters().isEmpty() ) { - return EMPTY; - } - else { - final int sqmParamCount = parameterResolutions.getSqmParameters().size(); - final LinkedHashMap, List>> sqmParamsByQueryParam = - new LinkedHashMap<>( sqmParamCount ); - final IdentityHashMap, QueryParameterImplementor> queryParamBySqmParam = - new IdentityHashMap<>( sqmParamCount ); - - for ( SqmParameter sqmParameter : parameterResolutions.getSqmParameters() ) { - if ( sqmParameter instanceof JpaCriteriaParameter ) { - // see discussion on `SqmJpaCriteriaParameterWrapper#accept` - throw new UnsupportedOperationException( - "Unexpected JpaCriteriaParameter in SqmStatement#getSqmParameters. Criteria parameters " + - "should be represented as SqmJpaCriteriaParameterWrapper references in this collection" - ); - } - - final QueryParameterImplementor queryParameter; - if ( sqmParameter.getName() != null ) { - queryParameter = QueryParameterNamedImpl.fromSqm( sqmParameter ); - } - else if ( sqmParameter.getPosition() != null ) { - queryParameter = QueryParameterPositionalImpl.fromSqm( sqmParameter ); - } - else if ( sqmParameter instanceof SqmJpaCriteriaParameterWrapper criteriaParameter ) { - if ( sqmParameter.allowMultiValuedBinding() - && sqmParameter.getExpressible() != null - && sqmParameter.getExpressible().getSqmType() instanceof BasicCollectionType ) { - // The wrapper parameter was inferred to be of a basic collection type, - // so we disallow multivalued bindings, because binding a list of collections isn't useful - criteriaParameter.getJpaCriteriaParameter().disallowMultiValuedBinding(); - } - queryParameter = QueryParameterIdentifiedImpl.fromSqm( criteriaParameter ); - } - else { - throw new UnsupportedOperationException( - "Unexpected SqmParameter type : " + sqmParameter ); - } - - sqmParamsByQueryParam.computeIfAbsent( queryParameter, impl -> new ArrayList<>() ).add( sqmParameter ); - queryParamBySqmParam.put( sqmParameter, queryParameter ); - } - - return new DomainParameterXref( sqmParamsByQueryParam, queryParamBySqmParam, parameterResolutions ); - } + final var parameterResolutions = sqmStatement.resolveParameters(); + final var parameters = parameterResolutions.getSqmParameters(); + return parameters.isEmpty() + ? EMPTY + : new DomainParameterXref( parameterResolutions, parameters ); } - // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Instance state @@ -100,28 +54,78 @@ else if ( sqmParameter instanceof SqmJpaCriteriaParameterWrapper criteriaPara private Map,List>> expansions; - private DomainParameterXref( - LinkedHashMap, List>> sqmParamsByQueryParam, - IdentityHashMap, QueryParameterImplementor> queryParamBySqmParam, - SqmStatement.ParameterResolutions parameterResolutions) { - this.sqmParamsByQueryParam = sqmParamsByQueryParam; - this.queryParamBySqmParam = queryParamBySqmParam; - this.parameterResolutions = parameterResolutions; + private DomainParameterXref() { + sqmParamsByQueryParam = new LinkedHashMap<>( 0 ); + queryParamBySqmParam = new IdentityHashMap<>( 0 ); + parameterResolutions = SqmStatement.ParameterResolutions.empty(); } - public DomainParameterXref copy() { + private DomainParameterXref(DomainParameterXref that) { + sqmParamsByQueryParam = that.sqmParamsByQueryParam; //noinspection unchecked - final var clone = + queryParamBySqmParam = (IdentityHashMap, QueryParameterImplementor>) - queryParamBySqmParam.clone(); - return new DomainParameterXref( sqmParamsByQueryParam, clone, parameterResolutions ); + that.queryParamBySqmParam.clone(); + parameterResolutions = that.parameterResolutions; + } + + private DomainParameterXref( + SqmStatement.ParameterResolutions resolutions, + Set> parameters) { + parameterResolutions = resolutions; + final int sqmParamCount = parameters.size(); + sqmParamsByQueryParam = new LinkedHashMap<>( sqmParamCount ); + queryParamBySqmParam = new IdentityHashMap<>( sqmParamCount ); + + for ( var parameter : parameters ) { + if ( parameter instanceof JpaCriteriaParameter ) { + // see discussion on `SqmJpaCriteriaParameterWrapper#accept` + throw new UnsupportedOperationException( + "Unexpected JpaCriteriaParameter (criteria parameters should be represented as SqmJpaCriteriaParameterWrapper references in this collection)" + ); + } + + final var queryParameter = fromSqm( parameter ); + sqmParamsByQueryParam.computeIfAbsent( queryParameter, impl -> new ArrayList<>() ) + .add( parameter ); + queryParamBySqmParam.put( parameter, queryParameter ); + } + } + + private static @NonNull QueryParameterImplementor fromSqm(SqmParameter sqmParameter) { + if ( sqmParameter.getName() != null ) { + return QueryParameterNamedImpl.fromSqm( sqmParameter ); + } + else if ( sqmParameter.getPosition() != null ) { + return QueryParameterPositionalImpl.fromSqm( sqmParameter ); + } + else if ( sqmParameter instanceof SqmJpaCriteriaParameterWrapper criteriaParameter ) { + if ( sqmParameter.allowMultiValuedBinding() ) { + final var expressible = sqmParameter.getExpressible(); + if ( expressible != null && expressible.getSqmType() instanceof BasicCollectionType ) { + // The wrapper parameter was inferred to be of a basic + // collection type, so we disallow multivalued bindings, + // because binding a list of collections isn't useful + criteriaParameter.getJpaCriteriaParameter().disallowMultiValuedBinding(); + } + } + return QueryParameterIdentifiedImpl.fromSqm( criteriaParameter ); + } + else { + throw new UnsupportedOperationException( "Unexpected SqmParameter type: " + sqmParameter ); + } + } + + public DomainParameterXref copy() { + return new DomainParameterXref( this ); } /** * Does this xref contain any parameters? */ public boolean hasParameters() { - return sqmParamsByQueryParam != null && ! sqmParamsByQueryParam.isEmpty(); + return sqmParamsByQueryParam != null + && ! sqmParamsByQueryParam.isEmpty(); } /** @@ -141,13 +145,6 @@ public int getSqmParameterCount() { return queryParamBySqmParam.size(); } -// public int getNumberOfSqmParameters(QueryParameterImplementor queryParameter) { -// final List> sqmParameters = sqmParamsByQueryParam.get( queryParameter ); -// return sqmParameters == null -// ? 0 // this should maybe be an exception instead -// : sqmParameters.size(); -// } - public SqmStatement.ParameterResolutions getParameterResolutions() { return parameterResolutions; } @@ -157,12 +154,9 @@ public List> getSqmParameters(QueryParameterImplementor query } public QueryParameterImplementor getQueryParameter(SqmParameter sqmParameter) { - if ( sqmParameter instanceof QueryParameterImplementor parameterImplementor ) { - return parameterImplementor; - } - else { - return queryParamBySqmParam.get( sqmParameter ); - } + return sqmParameter instanceof QueryParameterImplementor parameterImplementor + ? parameterImplementor + : queryParamBySqmParam.get( sqmParameter ); } public void addExpansion( @@ -182,15 +176,15 @@ public List> getExpansions(SqmParameter sqmParameter) { return emptyList(); } else { - final List> sqmParameters = expansions.get( sqmParameter ); + final var sqmParameters = expansions.get( sqmParameter ); return sqmParameters == null ? emptyList() : sqmParameters; } } public void clearExpansions() { if ( expansions != null ) { - for ( List> expansionList : expansions.values() ) { - for ( SqmParameter expansion : expansionList ) { + for ( var expansionList : expansions.values() ) { + for ( var expansion : expansionList ) { queryParamBySqmParam.remove( expansion ); } } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SqmQueryImpl.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SqmQueryImpl.java index 6c93e3fca5e4..80751516bee5 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SqmQueryImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SqmQueryImpl.java @@ -111,13 +111,13 @@ public class SqmQueryImpl implements SqmQueryImplementor, InterpretationsKeySource, DomainQueryExecutionContext { private final String hql; - private Object queryStringCacheKey; - private SqmStatement sqm; + private final Object queryStringCacheKey; + private final SqmStatement sqm; - private ParameterMetadataImplementor parameterMetadata; - private DomainParameterXref domainParameterXref; + private final ParameterMetadataImplementor parameterMetadata; + private final DomainParameterXref domainParameterXref; - private QueryParameterBindings parameterBindings; + private final QueryParameterBindings parameterBindings; private final Class resultType; private final TupleMetadata tupleMetadata; @@ -260,20 +260,20 @@ public SqmStatement getSqmStatement() { return sqm; } - @Override - protected void setSqmStatement(SqmSelectStatement sqm) { - this.sqm = sqm; - this.queryStringCacheKey = sqm; - - final var oldParameterBindings = parameterBindings; - domainParameterXref = DomainParameterXref.from( sqm ); - parameterMetadata = - domainParameterXref.hasParameters() - ? new ParameterMetadataImpl( domainParameterXref.getQueryParameters() ) - : ParameterMetadataImpl.EMPTY; - parameterBindings = parameterMetadata.createBindings( getSessionFactory() ); - copyParameterBindings( oldParameterBindings ); - } +// @Override +// protected void setSqmStatement(SqmSelectStatement sqm) { +// this.sqm = sqm; +// this.queryStringCacheKey = sqm; +// +// final var oldParameterBindings = parameterBindings; +// domainParameterXref = DomainParameterXref.from( sqm ); +// parameterMetadata = +// domainParameterXref.hasParameters() +// ? new ParameterMetadataImpl( domainParameterXref.getQueryParameters() ) +// : ParameterMetadataImpl.EMPTY; +// parameterBindings = parameterMetadata.createBindings( getSessionFactory() ); +// copyParameterBindings( oldParameterBindings ); +// } @Override public DomainParameterXref getDomainParameterXref() { @@ -516,28 +516,27 @@ protected int doExecuteUpdate() { private NonSelectQueryPlan resolveNonSelectQueryPlan() { // resolve (or make) the QueryPlan. - NonSelectQueryPlan queryPlan = null; - final var cacheKey = generateNonSelectKey( this ); final var interpretationCache = getInterpretationCache(); if ( cacheKey != null ) { - queryPlan = interpretationCache.getNonSelectQueryPlan( cacheKey ); - } - - if ( queryPlan == null ) { - queryPlan = buildNonSelectQueryPlan(); - if ( cacheKey != null ) { - interpretationCache.cacheNonSelectQueryPlan( cacheKey, queryPlan ); + final var queryPlan = interpretationCache.getNonSelectQueryPlan( cacheKey ); + if ( queryPlan != null ) { + return queryPlan; } } + + final var queryPlan = buildNonSelectQueryPlan(); + if ( cacheKey != null ) { + interpretationCache.cacheNonSelectQueryPlan( cacheKey, queryPlan ); + } return queryPlan; } private NonSelectQueryPlan buildNonSelectQueryPlan() { // to get here the SQM statement has already been validated to be // a non-select variety... - final SqmStatement sqmStatement = getSqmStatement(); + final var sqmStatement = getSqmStatement(); if ( sqmStatement instanceof SqmDeleteStatement ) { return buildDeleteQueryPlan(); } @@ -562,11 +561,11 @@ private NonSelectQueryPlan buildDeleteQueryPlan() { private NonSelectQueryPlan buildConcreteDeleteQueryPlan(SqmDeleteStatement deleteStatement) { final var entityDomainType = deleteStatement.getTarget().getModel(); - String entityDomainType1 = entityDomainType.getHibernateEntityName(); - final var persister = getMappingMetamodel().getEntityDescriptor( entityDomainType1 ); + final String entityName = entityDomainType.getHibernateEntityName(); + final var persister = getMappingMetamodel().getEntityDescriptor( entityName ); final var multiTableStrategy = persister.getSqmMultiTableMutationStrategy(); return multiTableStrategy != null - // NOTE : MultiTableDeleteQueryPlan and SqmMultiTableMutationStrategy already handle soft-deletes internally + // NOTE: MultiTableDeleteQueryPlan and SqmMultiTableMutationStrategy already handle soft-deletes internally ? new MultiTableDeleteQueryPlan( deleteStatement, domainParameterXref, multiTableStrategy ) : new SimpleDeleteQueryPlan( persister, deleteStatement, domainParameterXref ); } @@ -581,8 +580,8 @@ private NonSelectQueryPlan buildAggregatedDeleteQueryPlan(SqmDeleteStatement[ private NonSelectQueryPlan buildUpdateQueryPlan() { final var sqmUpdate = (SqmUpdateStatement) getSqmStatement(); - String entityDomainType = sqmUpdate.getTarget().getModel().getHibernateEntityName(); - final var persister = getMappingMetamodel().getEntityDescriptor( entityDomainType ); + final String entityName = sqmUpdate.getTarget().getModel().getHibernateEntityName(); + final var persister = getMappingMetamodel().getEntityDescriptor( entityName ); final var multiTableStrategy = persister.getSqmMultiTableMutationStrategy(); return multiTableStrategy == null ? new SimpleNonSelectQueryPlan( sqmUpdate, domainParameterXref ) @@ -591,8 +590,8 @@ private NonSelectQueryPlan buildUpdateQueryPlan() { private NonSelectQueryPlan buildInsertQueryPlan() { final var sqmInsert = (SqmInsertStatement) getSqmStatement(); - String entityDomainType = sqmInsert.getTarget().getModel().getHibernateEntityName(); - final var persister = getMappingMetamodel().getEntityDescriptor( entityDomainType ); + final String entityName = sqmInsert.getTarget().getModel().getHibernateEntityName(); + final var persister = getMappingMetamodel().getEntityDescriptor( entityName ); if ( useMultiTableInsert( persister, sqmInsert ) ) { return new MultiTableInsertQueryPlan( sqmInsert, diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SqmSelectionQueryImpl.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SqmSelectionQueryImpl.java index 073421682a0b..a01366873ae1 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SqmSelectionQueryImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SqmSelectionQueryImpl.java @@ -41,7 +41,6 @@ import org.hibernate.query.hql.internal.NamedHqlQueryMementoImpl; import org.hibernate.query.internal.DelegatingDomainQueryExecutionContext; import org.hibernate.query.internal.ParameterMetadataImpl; -import org.hibernate.type.BindableType; import org.hibernate.query.spi.DomainQueryExecutionContext; import org.hibernate.query.spi.HqlInterpretation; import org.hibernate.query.spi.ParameterMetadataImplementor; @@ -85,12 +84,12 @@ public class SqmSelectionQueryImpl extends AbstractSqmSelectionQuery implements SqmSelectionQueryImplementor, InterpretationsKeySource { private final String hql; - private Object queryStringCacheKey; - private SqmSelectStatement sqm; + private final Object queryStringCacheKey; + private final SqmSelectStatement sqm; - private ParameterMetadataImplementor parameterMetadata; - private DomainParameterXref domainParameterXref; - private QueryParameterBindings parameterBindings; + private final ParameterMetadataImplementor parameterMetadata; + private final DomainParameterXref domainParameterXref; + private final QueryParameterBindings parameterBindings; private final Class expectedResultType; private final Class resultType; @@ -262,16 +261,14 @@ private void setBindValues(QueryParameter parameter, QueryParameterBindin final var explicitTemporalPrecision = binding.getExplicitTemporalPrecision(); if ( explicitTemporalPrecision != null ) { if ( binding.isMultiValued() ) { - parameterBinding.setBindValues( binding.getBindValues(), explicitTemporalPrecision, - getTypeConfiguration() ); + parameterBinding.setBindValues( binding.getBindValues(), explicitTemporalPrecision ); } else { parameterBinding.setBindValue( binding.getBindValue(), explicitTemporalPrecision ); } } else { - //noinspection unchecked - final var bindType = (BindableType) binding.getBindType(); + final var bindType = binding.getBindType(); if ( binding.isMultiValued() ) { parameterBinding.setBindValues( binding.getBindValues(), bindType ); } @@ -324,20 +321,20 @@ public SqmSelectStatement getSqmStatement() { return sqm; } - @Override - protected void setSqmStatement(SqmSelectStatement sqm) { - this.sqm = sqm; - this.queryStringCacheKey = sqm; - - final QueryParameterBindings oldParameterBindings = parameterBindings; - domainParameterXref = DomainParameterXref.from( sqm ); - parameterMetadata = - domainParameterXref.hasParameters() - ? new ParameterMetadataImpl( domainParameterXref.getQueryParameters() ) - : ParameterMetadataImpl.EMPTY; - parameterBindings = parameterMetadata.createBindings( getSessionFactory() ); - copyParameterBindings( oldParameterBindings ); - } +// @Override +// protected void setSqmStatement(SqmSelectStatement sqm) { +// this.sqm = sqm; +// this.queryStringCacheKey = sqm; +// +// final QueryParameterBindings oldParameterBindings = parameterBindings; +// domainParameterXref = DomainParameterXref.from( sqm ); +// parameterMetadata = +// domainParameterXref.hasParameters() +// ? new ParameterMetadataImpl( domainParameterXref.getQueryParameters() ) +// : ParameterMetadataImpl.EMPTY; +// parameterBindings = parameterMetadata.createBindings( getSessionFactory() ); +// copyParameterBindings( oldParameterBindings ); +// } @Override public DomainParameterXref getDomainParameterXref() { diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/various/TimestampTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/various/TimestampTest.java index efa56c34d3c8..417bf08e1be2 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/various/TimestampTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/various/TimestampTest.java @@ -8,12 +8,9 @@ import org.hibernate.boot.registry.StandardServiceRegistry; import org.hibernate.boot.registry.StandardServiceRegistryBuilder; import org.hibernate.boot.spi.MetadataImplementor; -import org.hibernate.mapping.PersistentClass; -import org.hibernate.mapping.Property; import org.hibernate.testing.orm.junit.BaseUnitTest; import org.hibernate.testing.util.ServiceRegistryUtil; import org.hibernate.type.BasicType; -import org.hibernate.type.BasicTypeReference; import org.hibernate.type.StandardBasicTypes; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; @@ -49,26 +46,28 @@ public void tearDown() { } @Test - public void testTimestampSourceIsVM() throws Exception { - assertTimestampSource( VMTimestamped.class, StandardBasicTypes.TIMESTAMP ); + public void testTimestampSourceIsVM() { + assertTimestampSource( VMTimestamped.class ); } @Test - public void testTimestampSourceIsDB() throws Exception { - assertTimestampSource( DBTimestamped.class, StandardBasicTypes.TIMESTAMP ); + public void testTimestampSourceIsDB() { + assertTimestampSource( DBTimestamped.class ); } - private void assertTimestampSource(Class clazz, BasicTypeReference typeReference) throws Exception { - assertTimestampSource( clazz, metadata.getTypeConfiguration().getBasicTypeRegistry().resolve( typeReference ) ); + private void assertTimestampSource(Class clazz ) { + assertTimestampSource( clazz, + metadata.getTypeConfiguration().getBasicTypeRegistry() + .resolve( StandardBasicTypes.TIMESTAMP ) ); } - private void assertTimestampSource(Class clazz, BasicType basicType) throws Exception { - PersistentClass persistentClass = metadata.getEntityBinding( clazz.getName() ); + private void assertTimestampSource(Class clazz, BasicType basicType) { + var persistentClass = metadata.getEntityBinding( clazz.getName() ); assertThat( persistentClass ).isNotNull(); - Property versionProperty = persistentClass.getVersion(); + var versionProperty = persistentClass.getVersion(); assertThat( versionProperty ).isNotNull(); - assertThat( versionProperty.getType() ) + assertThat( versionProperty.getType().getName() ) .describedAs( "Wrong timestamp type" ) - .isEqualTo( basicType ); + .isEqualTo( basicType.getName() ); } } diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/procedure/StoredProcedureParameterTypeTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/procedure/StoredProcedureParameterTypeTest.java index a91a8b931a1e..eb9ebcb9af58 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/procedure/StoredProcedureParameterTypeTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/procedure/StoredProcedureParameterTypeTest.java @@ -452,7 +452,7 @@ public void testTypedParameterValueInParameterWithNotSpecifiedType(SessionFactor procedureCall.setParameter( 1, TypedParameterValue.of( StandardBasicTypes.INTEGER, 1 ) ); } catch (IllegalArgumentException e) { - assertTrue( e.getMessage().contains( "was not of specified type" ) ); + assertTrue( e.getMessage().contains( "incompatible" ) ); } } );