Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
*/
package org.hibernate.query.internal;

import org.hibernate.query.BindableType;
import org.hibernate.query.named.NamedQueryMemento;
import org.hibernate.query.spi.AbstractQueryParameter;
import org.hibernate.query.sqm.tree.expression.SqmJpaCriteriaParameterWrapper;

import org.checkerframework.checker.nullness.qual.Nullable;


/**
* QueryParameter impl for unnamed JPA Criteria-parameters.
*/
public class QueryParameterIdentifiedImpl<T> extends AbstractQueryParameter<T> {
/**
* Create an identified parameter descriptor from the SQM parameter
*
* @param parameter The source parameter info
*
* @return The parameter descriptor
*/
public static <T> QueryParameterIdentifiedImpl<T> fromSqm(SqmJpaCriteriaParameterWrapper<T> parameter) {
assert parameter.getName() == null;
assert parameter.getPosition() == null;
return new QueryParameterIdentifiedImpl<>(
parameter.getUnnamedParameterId(),
parameter.allowMultiValuedBinding(),
parameter.getAnticipatedType()
);
}

private final int unnamedParameterId;

private QueryParameterIdentifiedImpl(int unnamedParameterId, boolean allowMultiValuedBinding, @Nullable BindableType<T> anticipatedType) {
super( allowMultiValuedBinding, anticipatedType );
this.unnamedParameterId = unnamedParameterId;
}

public int getUnnamedParameterId() {
return unnamedParameterId;
}

@Override
public NamedQueryMemento.ParameterMemento toMemento() {
return session -> new QueryParameterIdentifiedImpl<>( unnamedParameterId, allowsMultiValuedBinding(), getHibernateType() );
}

@Override
public boolean equals(Object o) {
return this == o
|| o instanceof QueryParameterIdentifiedImpl<?>
&& unnamedParameterId == ( (QueryParameterIdentifiedImpl<?>) o ).unnamedParameterId;
}

@Override
public int hashCode() {
return unnamedParameterId;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -730,7 +730,7 @@ else if ( parameter.getPosition() != null ) {

protected <P> QueryParameterBinding<P> locateBinding(QueryParameterImplementor<P> parameter) {
getSession().checkOpen();
return getQueryParameterBindings().getBinding( parameter );
return getQueryParameterBindings().getBinding( getQueryParameter( parameter ) );
}

protected <P> QueryParameterBinding<P> locateBinding(String name) {
Expand All @@ -745,8 +745,12 @@ protected <P> QueryParameterBinding<P> locateBinding(int position) {

public boolean isBound(Parameter<?> param) {
getSession().checkOpen();
final QueryParameterImplementor<?> qp = getParameterMetadata().resolve( param );
return qp != null && getQueryParameterBindings().isBound( qp );
final QueryParameterImplementor<?> parameter = getParameterMetadata().resolve( param );
return parameter != null && getQueryParameterBindings().isBound( getQueryParameter( parameter ) );
}

protected <P> QueryParameterImplementor<P> getQueryParameter(QueryParameterImplementor<P> parameter) {
return parameter;
}

public <T> T getParameterValue(Parameter<T> param) {
Expand All @@ -759,7 +763,7 @@ public <T> T getParameterValue(Parameter<T> param) {
throw new IllegalArgumentException( "The parameter [" + param + "] is not part of this Query" );
}

final QueryParameterBinding<T> binding = getQueryParameterBindings().getBinding( qp );
final QueryParameterBinding<T> binding = getQueryParameterBindings().getBinding( getQueryParameter( qp ) );
if ( binding == null || !binding.isBound() ) {
throw new IllegalStateException( "Parameter value not yet bound : " + param.toString() );
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ public AbstractQueryParameter(
@Override
public void disallowMultiValuedBinding() {
QueryLogging.QUERY_MESSAGE_LOGGER.debugf( "QueryParameter#disallowMultiValuedBinding() called : %s", this );
this.allowMultiValuedBinding = true;
this.allowMultiValuedBinding = false;
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
import org.hibernate.query.QueryLogging;
import org.hibernate.query.SelectionQuery;
import org.hibernate.query.criteria.JpaSelection;
import org.hibernate.query.criteria.ValueHandlingMode;
import org.hibernate.query.hql.internal.NamedHqlQueryMementoImpl;
import org.hibernate.query.hql.internal.QuerySplitter;
import org.hibernate.query.named.NamedQueryMemento;
Expand All @@ -27,11 +26,13 @@
import org.hibernate.query.spi.QueryEngine;
import org.hibernate.query.spi.QueryInterpretationCache;
import org.hibernate.query.spi.QueryOptions;
import org.hibernate.query.spi.QueryParameterBindings;
import org.hibernate.query.spi.QueryParameterImplementor;
import org.hibernate.query.spi.SelectQueryPlan;
import org.hibernate.query.sqm.NodeBuilder;
import org.hibernate.query.sqm.SqmQuerySource;
import org.hibernate.query.sqm.spi.NamedSqmQueryMemento;
import org.hibernate.query.sqm.tree.SqmStatement;
import org.hibernate.query.sqm.tree.expression.JpaCriteriaParameter;
import org.hibernate.query.sqm.tree.expression.SqmJpaCriteriaParameterWrapper;
import org.hibernate.query.sqm.tree.from.SqmRoot;
import org.hibernate.query.sqm.tree.select.SqmQueryGroup;
import org.hibernate.query.sqm.tree.select.SqmQueryPart;
Expand All @@ -53,7 +54,6 @@
import static java.util.stream.Collectors.toList;
import static org.hibernate.cfg.QuerySettings.FAIL_ON_PAGINATION_OVER_COLLECTION_FETCH;
import static org.hibernate.query.KeyedPage.KeyInterpretation.KEY_OF_FIRST_ON_NEXT_PAGE;
import static org.hibernate.query.sqm.internal.KeyBasedPagination.paginate;
import static org.hibernate.query.sqm.internal.KeyedResult.collectKeys;
import static org.hibernate.query.sqm.internal.KeyedResult.collectResults;
import static org.hibernate.query.sqm.internal.SqmUtil.isHqlTuple;
Expand Down Expand Up @@ -129,6 +129,44 @@ private SqmSelectStatement<R> getSqmSelectStatement() {
}
}

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<?> ) {
final SqmJpaCriteriaParameterWrapper<?> wrapper = (SqmJpaCriteriaParameterWrapper<?>) sqmParameter;
@SuppressWarnings("unchecked")
final var criteriaParameter = (JpaCriteriaParameter<Object>) wrapper.getJpaCriteriaParameter();
final var value = criteriaParameter.getValue();
// We don't set a null value, unless the type is also null which
// is the case when using HibernateCriteriaBuilder.value
if ( value != null || criteriaParameter.getNodeType() == null ) {
// Use the anticipated type for binding the value if possible
//noinspection unchecked
final var parameter = (QueryParameterImplementor<Object>) entry.getKey();
bindings.getBinding( parameter )
.setBindValue( value, criteriaParameter.getAnticipatedType() );
}
}
}
}

@Override
protected <P> QueryParameterImplementor<P> getQueryParameter(QueryParameterImplementor<P> parameter) {
if ( parameter instanceof JpaCriteriaParameter<?> ) {
final JpaCriteriaParameter<?> criteriaParameter = (JpaCriteriaParameter<?>) parameter;
final var parameterWrapper = getDomainParameterXref().getParameterResolutions()
.getJpaCriteriaParamResolutions()
.get( criteriaParameter );
//noinspection unchecked
return (QueryParameterImplementor<P>) getDomainParameterXref().getQueryParameter( parameterWrapper );
}
else {
return parameter;
}
}

@Override
public SelectionQuery<R> setOrder(List<Order<? super R>> orderList) {
SqmSelectStatement<R> sqm = getSqmSelectStatement();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import java.util.Map;
import java.util.TreeMap;

import org.hibernate.query.internal.QueryParameterIdentifiedImpl;
import org.hibernate.query.internal.QueryParameterNamedImpl;
import org.hibernate.query.internal.QueryParameterPositionalImpl;
import org.hibernate.query.spi.QueryParameterImplementor;
Expand Down Expand Up @@ -41,14 +42,6 @@ public class DomainParameterXref {
* Create a DomainParameterXref for the parameters defined in the SQM statement
*/
public static DomainParameterXref from(SqmStatement<?> sqmStatement) {
// `xrefMap` is used to help maintain the proper cardinality between an
// SqmParameter and a QueryParameter. Multiple SqmParameter references
// can map to the same QueryParameter. Consider, e.g.,
// `.. where a.b = :param or a.c = :param`. Here we have 2 SqmParameter
// references (one for each occurrence of `:param`) both of which map to
// the same QueryParameter.
final Map<SqmParameter<?>, QueryParameterImplementor<?>> xrefMap = new TreeMap<>();

final SqmStatement.ParameterResolutions parameterResolutions = sqmStatement.resolveParameters();
if ( parameterResolutions.getSqmParameters().isEmpty() ) {
return EMPTY;
Expand All @@ -67,41 +60,32 @@ public static DomainParameterXref from(SqmStatement<?> sqmStatement) {
);
}

final QueryParameterImplementor<?> queryParameter = xrefMap.computeIfAbsent(
sqmParameter,
p -> {
if ( sqmParameter instanceof SqmJpaCriteriaParameterWrapper ) {
return ( (SqmJpaCriteriaParameterWrapper<?>) sqmParameter ).getJpaCriteriaParameter();
}
else if ( sqmParameter.getName() != null ) {
return QueryParameterNamedImpl.fromSqm( sqmParameter );
}
else if ( sqmParameter.getPosition() != null ) {
return QueryParameterPositionalImpl.fromSqm( sqmParameter );
}
else {
throw new UnsupportedOperationException( "Unexpected SqmParameter type : " + sqmParameter );
}
}
);

if ( ! sqmParameter.allowMultiValuedBinding() ) {
if ( queryParameter.allowsMultiValuedBinding() ) {
SqmTreeTransformationLogger.LOGGER.debugf(
"SqmParameter [%s] does not allow multi-valued binding, " +
"but mapped to existing QueryParameter [%s] that does - " +
"disallowing multi-valued binding" ,
sqmParameter,
queryParameter
);
queryParameter.disallowMultiValuedBinding();


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<?> ) {
final SqmJpaCriteriaParameterWrapper<?> criteriaParameter = (SqmJpaCriteriaParameterWrapper<?>) sqmParameter;
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 if ( sqmParameter.getExpressible() != null && sqmParameter.getExpressible().getSqmType() instanceof BasicCollectionType ) {
queryParameter.disallowMultiValuedBinding();
else {
throw new UnsupportedOperationException(
"Unexpected SqmParameter type : " + sqmParameter );
}

sqmParamsByQueryParam.computeIfAbsent( queryParameter, qp -> new ArrayList<>() ).add( sqmParameter );
sqmParamsByQueryParam.computeIfAbsent( queryParameter, impl -> new ArrayList<>() ).add( sqmParameter );
queryParamBySqmParam.put( sqmParameter, queryParameter );
}

Expand Down Expand Up @@ -177,10 +161,7 @@ public List<SqmParameter<?>> getSqmParameters(QueryParameterImplementor<?> query
}

public QueryParameterImplementor<?> getQueryParameter(SqmParameter<?> sqmParameter) {
if ( sqmParameter instanceof SqmJpaCriteriaParameterWrapper ) {
return ( (SqmJpaCriteriaParameterWrapper<?>) sqmParameter ).getJpaCriteriaParameter();
}
else if ( sqmParameter instanceof QueryParameterImplementor<?> ) {
if ( sqmParameter instanceof QueryParameterImplementor<?> ) {
return (QueryParameterImplementor<?>) sqmParameter;
}
return queryParamBySqmParam.get( sqmParameter );
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -226,11 +226,7 @@ public QuerySqmImpl(
this.parameterBindings = parameterMetadata.createBindings( producer.getFactory() );

// Parameters might be created through HibernateCriteriaBuilder.value which we need to bind here
for ( SqmParameter<?> sqmParameter : domainParameterXref.getParameterResolutions().getSqmParameters() ) {
if ( sqmParameter instanceof SqmJpaCriteriaParameterWrapper<?> ) {
bindCriteriaParameter((SqmJpaCriteriaParameterWrapper<?>) sqmParameter);
}
}
bindValueBindCriteriaParameters( domainParameterXref, parameterBindings );
if ( sqm instanceof SqmSelectStatement<?> ) {
final SqmSelectStatement<R> selectStatement = (SqmSelectStatement<R>) sqm;
final SqmQueryPart<R> queryPart = selectStatement.getQueryPart();
Expand All @@ -250,18 +246,6 @@ public QuerySqmImpl(
tupleMetadata = buildTupleMetadata( criteria, expectedResultType );
}

private <T> void bindCriteriaParameter(SqmJpaCriteriaParameterWrapper<T> sqmParameter) {
final JpaCriteriaParameter<T> jpaCriteriaParameter = sqmParameter.getJpaCriteriaParameter();
final T value = jpaCriteriaParameter.getValue();
// We don't set a null value, unless the type is also null which
// is the case when using HibernateCriteriaBuilder.value
if ( value != null || jpaCriteriaParameter.getNodeType() == null ) {
// Use the anticipated type for binding the value if possible
getQueryParameterBindings().getBinding( jpaCriteriaParameter )
.setBindValue( value, jpaCriteriaParameter.getAnticipatedType() );
}
}

@Override
public TupleMetadata getTupleMetadata() {
return tupleMetadata;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -169,11 +169,7 @@ public SqmSelectionQueryImpl(
this.parameterBindings = parameterMetadata.createBindings( session.getFactory() );

// Parameters might be created through HibernateCriteriaBuilder.value which we need to bind here
for ( SqmParameter<?> sqmParameter : domainParameterXref.getParameterResolutions().getSqmParameters() ) {
if ( sqmParameter instanceof SqmJpaCriteriaParameterWrapper<?> ) {
bindCriteriaParameter( (SqmJpaCriteriaParameterWrapper<?>) sqmParameter );
}
}
bindValueBindCriteriaParameters( domainParameterXref, parameterBindings );

this.expectedResultType = expectedResultType;
this.resultType = determineResultType( sqm, expectedResultType );
Expand Down Expand Up @@ -252,11 +248,7 @@ <E> SqmSelectionQueryImpl(AbstractSqmSelectionQuery<?> original, KeyedPage<E> ke
);

// Parameters might be created through HibernateCriteriaBuilder.value which we need to bind here
for ( SqmParameter<?> sqmParameter : domainParameterXref.getParameterResolutions().getSqmParameters() ) {
if ( sqmParameter instanceof SqmJpaCriteriaParameterWrapper<?> ) {
bindCriteriaParameter( (SqmJpaCriteriaParameterWrapper<?>) sqmParameter );
}
}
bindValueBindCriteriaParameters( domainParameterXref, parameterBindings );

//noinspection unchecked
this.expectedResultType = (Class<R>) KeyedResult.class;
Expand Down Expand Up @@ -300,19 +292,6 @@ else if ( expectedResultType != null ) {
}
}

private <T> void bindCriteriaParameter(SqmJpaCriteriaParameterWrapper<T> sqmParameter) {
final JpaCriteriaParameter<T> jpaCriteriaParameter = sqmParameter.getJpaCriteriaParameter();
final T value = jpaCriteriaParameter.getValue();
// We don't set a null value, unless the type is also null which
// is the case when using HibernateCriteriaBuilder.value
if ( value != null || jpaCriteriaParameter.getNodeType() == null ) {
// Use the anticipated type for binding the value if possible
getQueryParameterBindings()
.getBinding( jpaCriteriaParameter )
.setBindValue( value, jpaCriteriaParameter.getAnticipatedType() );
}
}

@Override
public TupleMetadata getTupleMetadata() {
return tupleMetadata;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5991,7 +5991,7 @@ public MappingModelExpressible<?> determineValueMapping(SqmExpression<?> sqmExpr

private MappingModelExpressible<?> determineValueMapping(SqmExpression<?> sqmExpression, FromClauseIndex fromClauseIndex) {
if ( sqmExpression instanceof SqmParameter ) {
return determineValueMapping( (SqmParameter<?>) sqmExpression );
return determineValueMapping( getSqmParameter( sqmExpression ) );
}

if ( sqmExpression instanceof SqmPath ) {
Expand Down Expand Up @@ -8222,13 +8222,14 @@ private InListPredicate processInSingleCriteriaParameter(
JpaCriteriaParameter<?> jpaCriteriaParameter) {
assert jpaCriteriaParameter.allowsMultiValuedBinding();

final QueryParameterBinding<?> domainParamBinding = domainParameterBindings.getBinding( jpaCriteriaParameter );
final SqmJpaCriteriaParameterWrapper<?> sqmWrapper = jpaCriteriaParamResolutions.get( jpaCriteriaParameter );
final QueryParameterImplementor<?> domainParam = domainParameterXref.getQueryParameter( sqmWrapper );
final QueryParameterBinding<?> domainParamBinding = domainParameterBindings.getBinding( domainParam );
if ( !domainParamBinding.isMultiValued() ) {
return null;
}
final SqmJpaCriteriaParameterWrapper<?> sqmWrapper = jpaCriteriaParamResolutions.get( jpaCriteriaParameter );

return processInSingleParameter( sqmPredicate, sqmWrapper, jpaCriteriaParameter, domainParamBinding );
return processInSingleParameter( sqmPredicate, sqmWrapper, domainParam, domainParamBinding );
}

@SuppressWarnings( "rawtypes" )
Expand Down
Loading
Loading