HHH-15895 - IllegalArgumentException :Cannot create binding for parameter referencen with criteria builder

This commit is contained in:
Steve Ebersole 2023-03-01 09:41:37 -06:00 committed by Christian Beikov
parent e65ded7a21
commit 7abc9f712c
8 changed files with 47 additions and 83 deletions

View File

@ -13,7 +13,6 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.TreeMap; import java.util.TreeMap;
import org.hibernate.HibernateException;
import org.hibernate.query.internal.QueryParameterNamedImpl; import org.hibernate.query.internal.QueryParameterNamedImpl;
import org.hibernate.query.internal.QueryParameterPositionalImpl; import org.hibernate.query.internal.QueryParameterPositionalImpl;
import org.hibernate.query.spi.QueryParameterImplementor; import org.hibernate.query.spi.QueryParameterImplementor;
@ -21,9 +20,7 @@ import org.hibernate.query.sqm.SqmTreeTransformationLogger;
import org.hibernate.query.sqm.tree.SqmStatement; import org.hibernate.query.sqm.tree.SqmStatement;
import org.hibernate.query.sqm.tree.expression.JpaCriteriaParameter; import org.hibernate.query.sqm.tree.expression.JpaCriteriaParameter;
import org.hibernate.query.sqm.tree.expression.SqmJpaCriteriaParameterWrapper; import org.hibernate.query.sqm.tree.expression.SqmJpaCriteriaParameterWrapper;
import org.hibernate.query.sqm.tree.expression.SqmNamedParameter;
import org.hibernate.query.sqm.tree.expression.SqmParameter; import org.hibernate.query.sqm.tree.expression.SqmParameter;
import org.hibernate.query.sqm.tree.expression.SqmPositionalParameter;
/** /**
* Maintains a cross-reference between SqmParameter and QueryParameter references. * Maintains a cross-reference between SqmParameter and QueryParameter references.
@ -32,8 +29,7 @@ import org.hibernate.query.sqm.tree.expression.SqmPositionalParameter;
*/ */
public class DomainParameterXref { public class DomainParameterXref {
/** /**
* Create a DomainParameterXref for the parameters defined in the passed * Create a DomainParameterXref for the parameters defined in the SQM statement
* SQM statement
*/ */
public static DomainParameterXref from(SqmStatement<?> sqmStatement) { public static DomainParameterXref from(SqmStatement<?> sqmStatement) {
// `xrefMap` is used to help maintain the proper cardinality between an // `xrefMap` is used to help maintain the proper cardinality between an
@ -42,10 +38,7 @@ public class DomainParameterXref {
// `.. where a.b = :param or a.c = :param`. Here we have 2 SqmParameter // `.. 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 // references (one for each occurrence of `:param`) both of which map to
// the same QueryParameter. // the same QueryParameter.
final Map<SqmParameter, QueryParameterImplementor<?>> xrefMap = new TreeMap<>( final Map<SqmParameter<?>, QueryParameterImplementor<?>> xrefMap = new TreeMap<>();
(o1, o2) ->
o1.compare( o2 )
);
final SqmStatement.ParameterResolutions parameterResolutions = sqmStatement.resolveParameters(); final SqmStatement.ParameterResolutions parameterResolutions = sqmStatement.resolveParameters();
if ( parameterResolutions.getSqmParameters().isEmpty() ) { if ( parameterResolutions.getSqmParameters().isEmpty() ) {

View File

@ -166,9 +166,9 @@ public class JpaCriteriaParameter<T>
} }
@Override @Override
public int compare(SqmParameter anotherParameter) { public int compareTo(SqmParameter anotherParameter) {
return anotherParameter instanceof JpaCriteriaParameter ? return anotherParameter instanceof JpaCriteriaParameter
Integer.compare( hashCode(), anotherParameter.hashCode() ) ? Integer.compare( hashCode(), anotherParameter.hashCode() )
: 1; : 1;
} }
} }

View File

@ -124,35 +124,9 @@ public class SqmJpaCriteriaParameterWrapper<T>
} }
@Override @Override
public int compare(SqmParameter anotherParameter) { public int compareTo(SqmParameter anotherParameter) {
return anotherParameter instanceof SqmJpaCriteriaParameterWrapper ? return anotherParameter instanceof SqmJpaCriteriaParameterWrapper ?
getJpaCriteriaParameter().compare( ( (SqmJpaCriteriaParameterWrapper<?>) anotherParameter ).getJpaCriteriaParameter() ) getJpaCriteriaParameter().compareTo( ( (SqmJpaCriteriaParameterWrapper<?>) anotherParameter ).getJpaCriteriaParameter() )
: 1; : 1;
} }
// @Override
// public Expression toSqlExpression(
// Clause clause,
// SqmToSqlAstConverter walker,
// SqlAstCreationState sqlAstCreationState) {
//
// final MappingModelExpressible mappingModelExpressible = DomainModelHelper.resolveMappingModelExpressible(
// jpaCriteriaParameter,
// sqlAstCreationState
// );
//
// final List<JdbcMapping> jdbcMappings = mappingModelExpressible.getJdbcMappings(
// sqlAstCreationState.getCreationContext().getDomainModel().getTypeConfiguration()
// );
//
// if ( jdbcMappings.size() == 1 ) {
// return new JdbcParameterImpl( jdbcMappings.get( 0 ) );
// }
//
// final SqlTuple.Builder tupleBuilder = new SqlTuple.Builder( mappingModelExpressible );
// for ( JdbcMapping jdbcMapping : jdbcMappings ) {
// tupleBuilder.addSubExpression( new JdbcParameterImpl( jdbcMapping ) );
// }
// return tupleBuilder.buildTuple();
// }
} }

View File

@ -83,7 +83,7 @@ public class SqmNamedParameter<T> extends AbstractSqmParameter<T> {
} }
@Override @Override
public int compare(SqmParameter anotherParameter) { public int compareTo(SqmParameter anotherParameter) {
return anotherParameter instanceof SqmNamedParameter<?> return anotherParameter instanceof SqmNamedParameter<?>
? getName().compareTo( ( (SqmNamedParameter<?>) anotherParameter ).getName() ) ? getName().compareTo( ( (SqmNamedParameter<?>) anotherParameter ).getName() )
: -1; : -1;

View File

@ -23,7 +23,7 @@ import org.hibernate.query.sqm.tree.SqmCopyContext;
* *
* @author Steve Ebersole * @author Steve Ebersole
*/ */
public interface SqmParameter<T> extends SqmExpression<T>, JpaParameterExpression<T> { public interface SqmParameter<T> extends SqmExpression<T>, JpaParameterExpression<T>, Comparable<SqmParameter<T>> {
/** /**
* If this represents a named parameter, return that parameter name; * If this represents a named parameter, return that parameter name;
* otherwise return {@code null}. * otherwise return {@code null}.
@ -76,20 +76,25 @@ public interface SqmParameter<T> extends SqmExpression<T>, JpaParameterExpressio
@Override @Override
SqmParameter<T> copy(SqmCopyContext context); SqmParameter<T> copy(SqmCopyContext context);
default int compare(SqmParameter anotherParameter) { /**
* @implSpec Defined as default since this is an SPI to
* support any previous extensions
*/
@Override
default int compareTo(SqmParameter<T> anotherParameter) {
if ( this instanceof SqmNamedParameter ) { if ( this instanceof SqmNamedParameter ) {
final SqmNamedParameter<?> one = (SqmNamedParameter<?>) this; final SqmNamedParameter<?> one = (SqmNamedParameter<?>) this;
return anotherParameter instanceof SqmNamedParameter<?> return anotherParameter instanceof SqmNamedParameter<?>
? one.getName().compareTo( ( (SqmNamedParameter<?>) anotherParameter ).getName() ) ? one.getName().compareTo( anotherParameter.getName() )
: -1; : -1;
} }
else if ( this instanceof SqmPositionalParameter ) { else if ( this instanceof SqmPositionalParameter ) {
final SqmPositionalParameter<?> one = (SqmPositionalParameter<?>) this; final SqmPositionalParameter<?> one = (SqmPositionalParameter<?>) this;
return anotherParameter instanceof SqmPositionalParameter<?> return anotherParameter instanceof SqmPositionalParameter<?>
? one.getPosition().compareTo( ( (SqmPositionalParameter<?>) anotherParameter ).getPosition() ) ? one.getPosition().compareTo( anotherParameter.getPosition() )
: 1; : 1;
} }
else if ( anotherParameter instanceof SqmJpaCriteriaParameterWrapper else if ( this instanceof SqmJpaCriteriaParameterWrapper
&& anotherParameter instanceof SqmJpaCriteriaParameterWrapper ) { && anotherParameter instanceof SqmJpaCriteriaParameterWrapper ) {
return Integer.compare( this.hashCode(), anotherParameter.hashCode() ); return Integer.compare( this.hashCode(), anotherParameter.hashCode() );
} }

View File

@ -86,7 +86,7 @@ public class SqmPositionalParameter<T> extends AbstractSqmParameter<T> {
} }
@Override @Override
public int compare(SqmParameter anotherParameter) { public int compareTo(SqmParameter anotherParameter) {
return anotherParameter instanceof SqmPositionalParameter<?> return anotherParameter instanceof SqmPositionalParameter<?>
? getPosition().compareTo( ( (SqmPositionalParameter<?>) anotherParameter ).getPosition() ) ? getPosition().compareTo( ( (SqmPositionalParameter<?>) anotherParameter ).getPosition() )
: 1; : 1;

View File

@ -6,8 +6,6 @@
*/ */
package org.hibernate.query.sqm.tree.expression; package org.hibernate.query.sqm.tree.expression;
import java.util.Objects;
import org.hibernate.query.BindableType; import org.hibernate.query.BindableType;
import org.hibernate.query.sqm.NodeBuilder; import org.hibernate.query.sqm.NodeBuilder;
import org.hibernate.query.sqm.tree.SqmCopyContext; import org.hibernate.query.sqm.tree.SqmCopyContext;
@ -54,10 +52,7 @@ public class ValueBindJpaCriteriaParameter<T> extends JpaCriteriaParameter<T>{
@Override @Override
public boolean equals(Object o) { public boolean equals(Object o) {
if ( this == o ) { return this == o;
return true;
}
return false;
} }
@Override @Override
@ -66,7 +61,7 @@ public class ValueBindJpaCriteriaParameter<T> extends JpaCriteriaParameter<T>{
} }
@Override @Override
public int compare(SqmParameter anotherParameter) { public int compareTo(SqmParameter anotherParameter) {
if ( this == anotherParameter ) { if ( this == anotherParameter ) {
return 0; return 0;
} }

View File

@ -9,15 +9,23 @@ package org.hibernate.orm.test.jpa.criteria;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import org.hibernate.dialect.DB2Dialect;
import org.hibernate.dialect.Dialect; import org.hibernate.dialect.Dialect;
import org.hibernate.dialect.H2Dialect; import org.hibernate.dialect.H2Dialect;
import org.hibernate.dialect.HSQLDialect; import org.hibernate.dialect.HSQLDialect;
import org.hibernate.dialect.MariaDBDialect; import org.hibernate.dialect.MariaDBDialect;
import org.hibernate.dialect.MySQLDialect; import org.hibernate.dialect.MySQLDialect;
import org.hibernate.dialect.SQLServerDialect;
import org.hibernate.dialect.SybaseDialect;
import org.hibernate.query.spi.QueryImplementor;
import org.hibernate.testing.TestForIssue; import org.hibernate.testing.TestForIssue;
import org.hibernate.testing.orm.junit.DomainModel;
import org.hibernate.testing.orm.junit.EntityManagerFactoryScope; import org.hibernate.testing.orm.junit.EntityManagerFactoryScope;
import org.hibernate.testing.orm.junit.Jpa; import org.hibernate.testing.orm.junit.Jpa;
import org.hibernate.testing.orm.junit.SessionFactory;
import org.hibernate.testing.orm.junit.SessionFactoryScope;
import org.hibernate.testing.orm.junit.SkipForDialect;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import jakarta.persistence.Entity; import jakarta.persistence.Entity;
@ -30,42 +38,31 @@ import jakarta.persistence.criteria.Root;
import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNotNull;
@Jpa(annotatedClasses = { /**
InPredicateTest.Event.class * @implNote Skipped for Dialects which do not support at lease
}) */
@TestForIssue(jiraKey = "HHH-15895") @TestForIssue(jiraKey = "HHH-15895")
@DomainModel(annotatedClasses = InPredicateTest.Event.class)
@SessionFactory(exportSchema = false)
public class InPredicateTest { public class InPredicateTest {
@Test @Test
public void testInPredicate(EntityManagerFactoryScope scope) { public void testInPredicate(SessionFactoryScope scope) {
scope.inTransaction( scope.inTransaction( (session) -> {
entityManager -> { CriteriaBuilder cb = session.getCriteriaBuilder();
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<Event> cr = cb.createQuery( Event.class ); CriteriaQuery<Event> cr = cb.createQuery( Event.class );
Root<Event> root = cr.from( Event.class ); Root<Event> root = cr.from( Event.class );
List<String> names = getNames( scope ); List<String> names = getNames();
cr.select( root ).where( root.get( "name" ).in( names ) ); cr.select( root ).where( root.get( "name" ).in( names ) );
List<Event> results = entityManager.createQuery( cr ).getResultList(); // This should trigger the error from HHH-15895 as QuerySqmImpl
assertNotNull( results ); // tries to handle the Criteria parameters
} session.createQuery( cr );
); } );
}
private List<String> getNames(EntityManagerFactoryScope scope) {
int maxNames;
Dialect dialect = scope.getDialect();
if ( dialect instanceof H2Dialect
|| dialect instanceof MariaDBDialect
|| dialect instanceof HSQLDialect
|| dialect instanceof MySQLDialect ) {
maxNames = 100000;
}
else {
// the other dialects does not support 100000 parameters
maxNames = 65500;
} }
private List<String> getNames() {
int maxNames = 100000;
List<String> names = new ArrayList<>( maxNames ); List<String> names = new ArrayList<>( maxNames );
for ( int i = 0; i < maxNames; i++ ) { for ( int i = 0; i < maxNames; i++ ) {
names.add( "abc" + i ); names.add( "abc" + i );