HHH-14876 Parameter padding for IN clauses doesn't work in Hibernate 6.0.0.Beta1
This commit is contained in:
parent
3f6a782760
commit
f9d0b7d069
|
@ -12,6 +12,7 @@ import org.hibernate.LockOptions;
|
|||
import org.hibernate.NotYetImplementedFor6Exception;
|
||||
import org.hibernate.dialect.sequence.NoSequenceSupport;
|
||||
import org.hibernate.engine.jdbc.dialect.spi.DialectResolutionInfo;
|
||||
import org.hibernate.internal.util.MathHelper;
|
||||
import org.hibernate.metamodel.mapping.JdbcMapping;
|
||||
import org.hibernate.query.FetchClauseType;
|
||||
import org.hibernate.query.IntervalType;
|
||||
|
@ -3068,7 +3069,18 @@ public abstract class Dialect implements ConversionContext {
|
|||
protected final BatchLoadSizingStrategy STANDARD_DEFAULT_BATCH_LOAD_SIZING_STRATEGY = new BatchLoadSizingStrategy() {
|
||||
@Override
|
||||
public int determineOptimalBatchLoadSize(int numberOfKeyColumns, int numberOfKeys) {
|
||||
return 50;
|
||||
int paddedSize = MathHelper.ceilingPowerOfTwo( numberOfKeys );
|
||||
// For tuples, there is no limit, so we can just use the power of two padding approach
|
||||
if ( numberOfKeyColumns > 1 ) {
|
||||
return paddedSize;
|
||||
}
|
||||
if ( paddedSize < getInExpressionCountLimit() ) {
|
||||
return paddedSize;
|
||||
}
|
||||
else if ( numberOfKeys < getInExpressionCountLimit() ) {
|
||||
return numberOfKeys;
|
||||
}
|
||||
return getInExpressionCountLimit();
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -13,6 +13,7 @@ import java.util.Collection;
|
|||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.IdentityHashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
@ -4866,17 +4867,17 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
|||
}
|
||||
|
||||
@Override
|
||||
public InListPredicate visitInListPredicate(SqmInListPredicate<?> predicate) {
|
||||
public Predicate visitInListPredicate(SqmInListPredicate<?> predicate) {
|
||||
// special case: if there is just a single "value" element and it is a parameter
|
||||
// and the binding for that parameter is multi-valued we need special
|
||||
// handling for "expansion"
|
||||
if ( predicate.getListExpressions().size() == 1 ) {
|
||||
final SqmExpression sqmExpression = predicate.getListExpressions().get( 0 );
|
||||
final SqmExpression<?> sqmExpression = predicate.getListExpressions().get( 0 );
|
||||
if ( sqmExpression instanceof SqmParameter ) {
|
||||
final SqmParameter sqmParameter = (SqmParameter) sqmExpression;
|
||||
final SqmParameter<?> sqmParameter = (SqmParameter<?>) sqmExpression;
|
||||
|
||||
if ( sqmParameter.allowMultiValuedBinding() ) {
|
||||
final InListPredicate specialCase = processInListWithSingleParameter( predicate, sqmParameter );
|
||||
final Predicate specialCase = processInListWithSingleParameter( predicate, sqmParameter );
|
||||
if ( specialCase != null ) {
|
||||
return specialCase;
|
||||
}
|
||||
|
@ -4895,7 +4896,7 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
|||
inferrableTypeAccessStack.push( () -> determineValueMapping( predicate.getTestExpression() ) );
|
||||
|
||||
try {
|
||||
for ( SqmExpression expression : predicate.getListExpressions() ) {
|
||||
for ( SqmExpression<?> expression : predicate.getListExpressions() ) {
|
||||
inPredicate.addExpression( (Expression) expression.accept( this ) );
|
||||
}
|
||||
}
|
||||
|
@ -4906,70 +4907,49 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
|||
return inPredicate;
|
||||
}
|
||||
|
||||
private InListPredicate processInListWithSingleParameter(
|
||||
private Predicate processInListWithSingleParameter(
|
||||
SqmInListPredicate<?> sqmPredicate,
|
||||
SqmParameter sqmParameter) {
|
||||
SqmParameter<?> sqmParameter) {
|
||||
assert sqmParameter.allowMultiValuedBinding();
|
||||
|
||||
if ( sqmParameter instanceof JpaCriteriaParameter ) {
|
||||
return processInSingleCriteriaParameter( sqmPredicate, (JpaCriteriaParameter) sqmParameter );
|
||||
return processInSingleCriteriaParameter( sqmPredicate, (JpaCriteriaParameter<?>) sqmParameter );
|
||||
}
|
||||
|
||||
return processInSingleHqlParameter( sqmPredicate, sqmParameter );
|
||||
}
|
||||
|
||||
private InListPredicate processInSingleHqlParameter(SqmInListPredicate<?> sqmPredicate, SqmParameter sqmParameter) {
|
||||
private Predicate processInSingleHqlParameter(SqmInListPredicate<?> sqmPredicate, SqmParameter<?> sqmParameter) {
|
||||
final QueryParameterImplementor<?> domainParam = domainParameterXref.getQueryParameter( sqmParameter );
|
||||
final QueryParameterBinding domainParamBinding = domainParameterBindings.getBinding( domainParam );
|
||||
|
||||
final QueryParameterBinding<?> domainParamBinding = domainParameterBindings.getBinding( domainParam );
|
||||
if ( !domainParamBinding.isMultiValued() ) {
|
||||
// triggers normal processing
|
||||
return null;
|
||||
}
|
||||
|
||||
final InListPredicate inListPredicate = new InListPredicate(
|
||||
(Expression) sqmPredicate.getTestExpression().accept( this ),
|
||||
sqmPredicate.isNegated(),
|
||||
getBooleanType()
|
||||
);
|
||||
|
||||
inferrableTypeAccessStack.push(
|
||||
() -> determineValueMapping( sqmPredicate.getTestExpression() )
|
||||
);
|
||||
|
||||
try {
|
||||
boolean first = true;
|
||||
for ( Object bindValue : domainParamBinding.getBindValues() ) {
|
||||
final SqmParameter sqmParamToConsume;
|
||||
// for each bind value create an "expansion"
|
||||
if ( first ) {
|
||||
sqmParamToConsume = sqmParameter;
|
||||
first = false;
|
||||
}
|
||||
else {
|
||||
sqmParamToConsume = sqmParameter.copy();
|
||||
domainParameterXref.addExpansion( domainParam, sqmParameter, sqmParamToConsume );
|
||||
return processInSingleParameter( sqmPredicate, sqmParameter, domainParam, domainParamBinding );
|
||||
}
|
||||
|
||||
inListPredicate.addExpression( consumeSingleSqmParameter( sqmParamToConsume ) );
|
||||
}
|
||||
}
|
||||
finally {
|
||||
inferrableTypeAccessStack.pop();
|
||||
}
|
||||
|
||||
return inListPredicate;
|
||||
}
|
||||
|
||||
private InListPredicate processInSingleCriteriaParameter(
|
||||
private Predicate processInSingleCriteriaParameter(
|
||||
SqmInListPredicate<?> sqmPredicate,
|
||||
JpaCriteriaParameter jpaCriteriaParameter) {
|
||||
JpaCriteriaParameter<?> jpaCriteriaParameter) {
|
||||
assert jpaCriteriaParameter.allowsMultiValuedBinding();
|
||||
|
||||
final QueryParameterBinding domainParamBinding = domainParameterBindings.getBinding( jpaCriteriaParameter );
|
||||
final QueryParameterBinding<?> domainParamBinding = domainParameterBindings.getBinding( jpaCriteriaParameter );
|
||||
if ( !domainParamBinding.isMultiValued() ) {
|
||||
return null;
|
||||
}
|
||||
final SqmJpaCriteriaParameterWrapper<?> sqmWrapper = jpaCriteriaParamResolutions.get( jpaCriteriaParameter )
|
||||
.get();
|
||||
|
||||
return processInSingleParameter( sqmPredicate, sqmWrapper, jpaCriteriaParameter, domainParamBinding );
|
||||
}
|
||||
|
||||
private Predicate processInSingleParameter(
|
||||
SqmInListPredicate<?> sqmPredicate,
|
||||
SqmParameter<?> sqmParameter,
|
||||
QueryParameterImplementor<?> domainParam,
|
||||
QueryParameterBinding<?> domainParamBinding) {
|
||||
|
||||
final InListPredicate inListPredicate = new InListPredicate(
|
||||
(Expression) sqmPredicate.getTestExpression().accept( this ),
|
||||
|
@ -4981,31 +4961,22 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
|||
() -> determineValueMapping( sqmPredicate.getTestExpression() )
|
||||
);
|
||||
|
||||
final SqmJpaCriteriaParameterWrapper<?> sqmWrapper = jpaCriteriaParamResolutions.get( jpaCriteriaParameter )
|
||||
.get();
|
||||
|
||||
try {
|
||||
boolean first = true;
|
||||
for ( Object bindValue : domainParamBinding.getBindValues() ) {
|
||||
final SqmParameter sqmParamToConsume;
|
||||
final Iterator<?> iterator = domainParamBinding.getBindValues().iterator();
|
||||
inListPredicate.addExpression( consumeSingleSqmParameter( sqmParameter ) );
|
||||
iterator.next();
|
||||
while ( iterator.hasNext() ) {
|
||||
iterator.next();
|
||||
// for each bind value create an "expansion"
|
||||
if ( first ) {
|
||||
sqmParamToConsume = sqmWrapper;
|
||||
first = false;
|
||||
}
|
||||
else {
|
||||
sqmParamToConsume = sqmWrapper.copy();
|
||||
domainParameterXref.addExpansion( jpaCriteriaParameter, sqmWrapper, sqmParamToConsume );
|
||||
}
|
||||
|
||||
final SqmParameter<?> sqmParamToConsume = sqmParameter.copy();
|
||||
domainParameterXref.addExpansion( domainParam, sqmParameter, sqmParamToConsume );
|
||||
inListPredicate.addExpression( consumeSingleSqmParameter( sqmParamToConsume ) );
|
||||
}
|
||||
return inListPredicate;
|
||||
}
|
||||
finally {
|
||||
inferrableTypeAccessStack.pop();
|
||||
}
|
||||
|
||||
return inListPredicate;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -15,6 +15,7 @@ import java.util.Collections;
|
|||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.IdentityHashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
@ -26,12 +27,14 @@ import org.hibernate.LockMode;
|
|||
import org.hibernate.QueryException;
|
||||
import org.hibernate.dialect.RowLockStrategy;
|
||||
import org.hibernate.dialect.SelectItemReferenceStrategy;
|
||||
import org.hibernate.internal.util.MathHelper;
|
||||
import org.hibernate.metamodel.mapping.EntityAssociationMapping;
|
||||
import org.hibernate.metamodel.mapping.ModelPart;
|
||||
import org.hibernate.persister.entity.EntityPersister;
|
||||
import org.hibernate.persister.entity.Queryable;
|
||||
import org.hibernate.query.IllegalQueryOperationException;
|
||||
import org.hibernate.query.sqm.sql.internal.SqmPathInterpretation;
|
||||
import org.hibernate.query.sqm.tree.expression.SqmParameter;
|
||||
import org.hibernate.sql.ast.tree.cte.CteMaterialization;
|
||||
import org.hibernate.sql.ast.tree.cte.CteSearchClauseKind;
|
||||
import org.hibernate.query.FetchClauseType;
|
||||
|
@ -4081,22 +4084,12 @@ public abstract class AbstractSqlAstTranslator<T extends JdbcOperation> implemen
|
|||
appendSql( "1=0" );
|
||||
return;
|
||||
}
|
||||
Function<Expression, Expression> itemAccessor = Function.identity();
|
||||
final SqlTuple lhsTuple;
|
||||
if ( ( lhsTuple = SqlTupleContainer.getSqlTuple( inListPredicate.getTestExpression() ) ) != null ) {
|
||||
if ( lhsTuple.getExpressions().size() == 1 ) {
|
||||
// Special case for tuples with arity 1 as any DBMS supports scalar IN predicates
|
||||
lhsTuple.getExpressions().get( 0 ).accept( this );
|
||||
if ( inListPredicate.isNegated() ) {
|
||||
appendSql( " not" );
|
||||
}
|
||||
appendSql( " in(" );
|
||||
String separator = NO_SEPARATOR;
|
||||
for ( Expression expression : listExpressions ) {
|
||||
appendSql( separator );
|
||||
SqlTupleContainer.getSqlTuple( expression ).getExpressions().get( 0 ).accept( this );
|
||||
separator = COMA_SEPARATOR;
|
||||
}
|
||||
appendSql( CLOSE_PARENTHESIS );
|
||||
itemAccessor = listExpression -> SqlTupleContainer.getSqlTuple( listExpression ).getExpressions().get( 0 );
|
||||
}
|
||||
else if ( !supportsRowValueConstructorSyntaxInInList() ) {
|
||||
final ComparisonOperator comparisonOperator = inListPredicate.isNegated() ?
|
||||
|
@ -4132,26 +4125,108 @@ public abstract class AbstractSqlAstTranslator<T extends JdbcOperation> implemen
|
|||
separator = " or ";
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
else {
|
||||
}
|
||||
|
||||
inListPredicate.getTestExpression().accept( this );
|
||||
if ( inListPredicate.isNegated() ) {
|
||||
appendSql( " not" );
|
||||
}
|
||||
appendSql( " in(" );
|
||||
renderCommaSeparated( listExpressions );
|
||||
appendSql( CLOSE_PARENTHESIS );
|
||||
}
|
||||
String separator = NO_SEPARATOR;
|
||||
|
||||
int bindValueCount = listExpressions.size();
|
||||
int bindValueMaxCount = bindValueCount;
|
||||
|
||||
final Dialect dialect = getSessionFactory().getJdbcServices().getDialect();
|
||||
int inExprLimit = dialect.getInExpressionCountLimit();
|
||||
|
||||
final boolean inClauseParameterPaddingEnabled = getSessionFactory().
|
||||
getSessionFactoryOptions().inClauseParameterPaddingEnabled()
|
||||
&& bindValueCount > 2;
|
||||
|
||||
if ( inClauseParameterPaddingEnabled ) {
|
||||
// bindValueCount: 1005
|
||||
// bindValuePaddingCount: 1024
|
||||
int bindValuePaddingCount = MathHelper.ceilingPowerOfTwo( bindValueCount );
|
||||
|
||||
// inExprLimit: 1000
|
||||
if ( inExprLimit > 0 ) {
|
||||
if ( bindValuePaddingCount > inExprLimit ) {
|
||||
// bindValueCount % inExprLimit: 5
|
||||
// bindValuePaddingCount: 8
|
||||
if ( bindValueCount < inExprLimit ) {
|
||||
bindValueMaxCount = inExprLimit;
|
||||
}
|
||||
else {
|
||||
bindValueMaxCount = MathHelper.ceilingPowerOfTwo( bindValueCount % inExprLimit );
|
||||
}
|
||||
}
|
||||
else if ( bindValueCount < bindValuePaddingCount ) {
|
||||
bindValueMaxCount = bindValuePaddingCount;
|
||||
}
|
||||
}
|
||||
else if ( bindValueCount < bindValuePaddingCount ) {
|
||||
bindValueMaxCount = bindValuePaddingCount;
|
||||
}
|
||||
}
|
||||
|
||||
final Iterator<Expression> iterator = listExpressions.iterator();
|
||||
int itemNumber = 0;
|
||||
while ( iterator.hasNext() && ( inExprLimit == 0 || itemNumber < inExprLimit ) ) {
|
||||
final Expression listExpression = itemAccessor.apply( iterator.next() );
|
||||
appendSql( separator );
|
||||
listExpression.accept( this );
|
||||
separator = COMA_SEPARATOR;
|
||||
itemNumber++;
|
||||
// If we encounter an expression that is not a parameter, we reset the inExprLimit and bindValueMaxCount
|
||||
// and just render through the in list expressions as they are without padding/splitting
|
||||
if ( !( listExpression instanceof JdbcParameter || listExpression instanceof SqmParameterInterpretation ) ) {
|
||||
inExprLimit = 0;
|
||||
bindValueMaxCount = bindValueCount;
|
||||
}
|
||||
}
|
||||
|
||||
if ( itemNumber != inExprLimit && bindValueCount == bindValueMaxCount ) {
|
||||
appendSql( CLOSE_PARENTHESIS );
|
||||
return;
|
||||
}
|
||||
|
||||
if ( inExprLimit > 0 && bindValueCount > inExprLimit ) {
|
||||
do {
|
||||
append( ") and " );
|
||||
inListPredicate.getTestExpression().accept( this );
|
||||
if ( inListPredicate.isNegated() ) {
|
||||
appendSql( " not" );
|
||||
}
|
||||
appendSql( " in(" );
|
||||
renderCommaSeparated( listExpressions );
|
||||
appendSql( CLOSE_PARENTHESIS );
|
||||
separator = NO_SEPARATOR;
|
||||
itemNumber = 0;
|
||||
while ( iterator.hasNext() && itemNumber < inExprLimit ) {
|
||||
final Expression listExpression = iterator.next();
|
||||
appendSql( separator );
|
||||
itemAccessor.apply( listExpression ).accept( this );
|
||||
separator = COMA_SEPARATOR;
|
||||
itemNumber++;
|
||||
}
|
||||
} while ( iterator.hasNext() );
|
||||
}
|
||||
|
||||
int i;
|
||||
if ( inExprLimit > 0 && bindValueCount > inExprLimit ) {
|
||||
i = bindValueCount % inExprLimit;
|
||||
}
|
||||
else {
|
||||
i = bindValueCount;
|
||||
}
|
||||
final Expression lastExpression = itemAccessor.apply( listExpressions.get( listExpressions.size() - 1 ) );
|
||||
for ( ; i < bindValueMaxCount; i++ ) {
|
||||
appendSql( separator );
|
||||
lastExpression.accept( this );
|
||||
separator = COMA_SEPARATOR;
|
||||
}
|
||||
appendSql( CLOSE_PARENTHESIS );
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -0,0 +1,135 @@
|
|||
/*
|
||||
* 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.orm.test.query;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.hibernate.cfg.AvailableSettings;
|
||||
|
||||
import org.hibernate.testing.TestForIssue;
|
||||
import org.hibernate.testing.jdbc.SQLStatementInspector;
|
||||
import org.hibernate.testing.orm.junit.EntityManagerFactoryScope;
|
||||
import org.hibernate.testing.orm.junit.Jpa;
|
||||
import org.hibernate.testing.orm.junit.Setting;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.criteria.CriteriaBuilder;
|
||||
import jakarta.persistence.criteria.CriteriaQuery;
|
||||
import jakarta.persistence.criteria.ParameterExpression;
|
||||
import jakarta.persistence.criteria.Root;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
|
||||
/**
|
||||
* @author Vlad Mihalcea
|
||||
*/
|
||||
@TestForIssue(jiraKey = "HHH-13108")
|
||||
@Jpa(
|
||||
annotatedClasses = InClauseParameterPaddingCriteriaTest.Document.class,
|
||||
integrationSettings = {
|
||||
@Setting(name = AvailableSettings.USE_SQL_COMMENTS, value = "true"),
|
||||
@Setting(name = AvailableSettings.IN_CLAUSE_PARAMETER_PADDING, value = "true"),
|
||||
},
|
||||
statementInspectorClass = SQLStatementInspector.class
|
||||
)
|
||||
public class InClauseParameterPaddingCriteriaTest {
|
||||
|
||||
@BeforeAll
|
||||
protected void afterEntityManagerFactoryBuilt(EntityManagerFactoryScope scope) {
|
||||
scope.inTransaction( entityManager -> {
|
||||
Document document = new Document();
|
||||
document.setName( "A" );
|
||||
entityManager.persist( document );
|
||||
} );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInClauseParameterPadding(EntityManagerFactoryScope scope) {
|
||||
final SQLStatementInspector statementInspector = (SQLStatementInspector) scope.getStatementInspector();
|
||||
statementInspector.clear();
|
||||
|
||||
scope.inTransaction( entityManager -> {
|
||||
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
|
||||
CriteriaQuery<Integer> query = cb.createQuery( Integer.class );
|
||||
Root<Document> document = query.from( Document.class );
|
||||
|
||||
ParameterExpression<List> inClauseParams = cb.parameter( List.class, "ids" );
|
||||
|
||||
query.select( document.get( "id" ) )
|
||||
.where( document.get( "id" ).in( inClauseParams ) );
|
||||
|
||||
List<Integer> ids = entityManager.createQuery( query )
|
||||
.setParameter( "ids", Arrays.asList( 1, 2, 3, 4, 5 ) )
|
||||
.getResultList();
|
||||
assertEquals( 1, ids.size() );
|
||||
} );
|
||||
|
||||
assertTrue( statementInspector.getSqlQueries().get( 0 ).endsWith( "in(?,?,?,?,?,?,?,?)" ) );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInClauseParameterPaddingForExpressions(EntityManagerFactoryScope scope) {
|
||||
final SQLStatementInspector statementInspector = (SQLStatementInspector) scope.getStatementInspector();
|
||||
statementInspector.clear();
|
||||
|
||||
scope.inTransaction( entityManager -> {
|
||||
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
|
||||
CriteriaQuery<Integer> query = cb.createQuery( Integer.class );
|
||||
Root<Document> document = query.from( Document.class );
|
||||
|
||||
query.select( document.get( "id" ) )
|
||||
.where(
|
||||
document.get( "id" ).in(
|
||||
document.get( "id" ),
|
||||
document.get( "id" ),
|
||||
document.get( "id" )
|
||||
)
|
||||
);
|
||||
|
||||
List<Integer> ids = entityManager.createQuery( query )
|
||||
.getResultList();
|
||||
assertEquals( 1, ids.size() );
|
||||
} );
|
||||
|
||||
assertTrue( statementInspector.getSqlQueries().get( 0 ).endsWith( "in(d1_0.id,d1_0.id,d1_0.id)" ) );
|
||||
}
|
||||
|
||||
@Entity(name = "Document")
|
||||
public static class Document {
|
||||
|
||||
@Id
|
||||
@GeneratedValue
|
||||
private Integer id;
|
||||
|
||||
private String name;
|
||||
|
||||
public Integer getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Integer id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,113 @@
|
|||
/*
|
||||
* 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.orm.test.query;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.hibernate.cfg.AvailableSettings;
|
||||
|
||||
import org.hibernate.testing.TestForIssue;
|
||||
import org.hibernate.testing.jdbc.SQLStatementInspector;
|
||||
import org.hibernate.testing.orm.junit.EntityManagerFactoryScope;
|
||||
import org.hibernate.testing.orm.junit.Jpa;
|
||||
import org.hibernate.testing.orm.junit.Setting;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.Id;
|
||||
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
|
||||
/**
|
||||
* @author Vlad Mihalcea
|
||||
*/
|
||||
@TestForIssue(jiraKey = "HHH-12469")
|
||||
@Jpa(
|
||||
annotatedClasses = { InClauseParameterPaddingTest.Person.class },
|
||||
integrationSettings = {
|
||||
@Setting(name = AvailableSettings.USE_SQL_COMMENTS, value = "true"),
|
||||
@Setting(name = AvailableSettings.IN_CLAUSE_PARAMETER_PADDING, value = "true")
|
||||
},
|
||||
statementInspectorClass = SQLStatementInspector.class
|
||||
|
||||
)
|
||||
public class InClauseParameterPaddingTest {
|
||||
|
||||
@BeforeEach
|
||||
protected void afterEntityManagerFactoryBuilt(EntityManagerFactoryScope scope) {
|
||||
scope.inTransaction( entityManager -> {
|
||||
for ( int i = 1; i < 10; i++ ) {
|
||||
Person person = new Person();
|
||||
person.setId( i );
|
||||
person.setName( String.format( "Person nr %d", i ) );
|
||||
|
||||
entityManager.persist( person );
|
||||
}
|
||||
} );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInClauseParameterPadding(EntityManagerFactoryScope scope) {
|
||||
validateInClauseParameterPadding( scope, "in(?)", 1 );
|
||||
validateInClauseParameterPadding( scope, "in(?,?)", 1, 2 );
|
||||
validateInClauseParameterPadding( scope, "in(?,?,?,?)", 1, 2, 3 );
|
||||
validateInClauseParameterPadding( scope, "in(?,?,?,?)", 1, 2, 3, 4 );
|
||||
validateInClauseParameterPadding( scope, "in(?,?,?,?,?,?,?,?)", 1, 2, 3, 4, 5 );
|
||||
validateInClauseParameterPadding( scope, "in(?,?,?,?,?,?,?,?)", 1, 2, 3, 4, 5, 6 );
|
||||
validateInClauseParameterPadding( scope, "in(?,?,?,?,?,?,?,?)", 1, 2, 3, 4, 5, 6, 7 );
|
||||
validateInClauseParameterPadding( scope, "in(?,?,?,?,?,?,?,?)", 1, 2, 3, 4, 5, 6, 7, 8 );
|
||||
validateInClauseParameterPadding( scope, "in(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)", 1, 2, 3, 4, 5, 6, 7, 8, 9 );
|
||||
validateInClauseParameterPadding( scope, "in(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)", 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 );
|
||||
}
|
||||
|
||||
private void validateInClauseParameterPadding(
|
||||
EntityManagerFactoryScope scope,
|
||||
String expectedInClause,
|
||||
Integer... ids) {
|
||||
final SQLStatementInspector sqlStatementInterceptor = (SQLStatementInspector) scope.getStatementInspector();
|
||||
sqlStatementInterceptor.clear();
|
||||
|
||||
scope.inTransaction( entityManager -> {
|
||||
entityManager.createQuery(
|
||||
"select p " +
|
||||
"from Person p " +
|
||||
"where p.id in :ids" )
|
||||
.setParameter( "ids", Arrays.asList( ids ) )
|
||||
.getResultList();
|
||||
} );
|
||||
|
||||
assertTrue( sqlStatementInterceptor.getSqlQueries().get( 0 ).endsWith( expectedInClause ) );
|
||||
}
|
||||
|
||||
@Entity(name = "Person")
|
||||
public static class Person {
|
||||
|
||||
@Id
|
||||
private Integer id;
|
||||
|
||||
private String name;
|
||||
|
||||
public Integer getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Integer id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,240 @@
|
|||
/*
|
||||
* 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.orm.test.query;
|
||||
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
import org.hibernate.cfg.AvailableSettings;
|
||||
import org.hibernate.dialect.H2Dialect;
|
||||
|
||||
import org.hibernate.testing.TestForIssue;
|
||||
import org.hibernate.testing.jdbc.SQLStatementInspector;
|
||||
import org.hibernate.testing.orm.jpa.NonStringValueSettingProvider;
|
||||
import org.hibernate.testing.orm.junit.EntityManagerFactoryScope;
|
||||
import org.hibernate.testing.orm.junit.Jpa;
|
||||
import org.hibernate.testing.orm.junit.RequiresDialect;
|
||||
import org.hibernate.testing.orm.junit.Setting;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.Id;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
/**
|
||||
* @author Vlad Mihalcea
|
||||
*/
|
||||
@TestForIssue(jiraKey = "HHH-12469")
|
||||
@RequiresDialect(H2Dialect.class)
|
||||
@Jpa(
|
||||
annotatedClasses = { MaxInExpressionParameterPaddingTest.Person.class },
|
||||
integrationSettings = {
|
||||
@Setting(name = AvailableSettings.USE_SQL_COMMENTS, value = "true"),
|
||||
@Setting(name = AvailableSettings.IN_CLAUSE_PARAMETER_PADDING, value = "true"),
|
||||
},
|
||||
nonStringValueSettingProviders = MaxInExpressionParameterPaddingTest.DialectProvider.class,
|
||||
statementInspectorClass = SQLStatementInspector.class
|
||||
)
|
||||
public class MaxInExpressionParameterPaddingTest {
|
||||
|
||||
public static class DialectProvider extends NonStringValueSettingProvider {
|
||||
@Override
|
||||
public String getKey() {
|
||||
return AvailableSettings.DIALECT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getValue() {
|
||||
return MaxInExpressionParameterPaddingTest.MaxCountInExpressionH2Dialect.class.getName();
|
||||
}
|
||||
}
|
||||
|
||||
public static final int MAX_COUNT = 15;
|
||||
|
||||
@BeforeAll
|
||||
protected void afterEntityManagerFactoryBuilt(EntityManagerFactoryScope scope) {
|
||||
scope.inTransaction( entityManager -> {
|
||||
for ( int i = 0; i < MAX_COUNT; i++ ) {
|
||||
Person person = new Person();
|
||||
person.setId( i );
|
||||
person.setName( String.format( "Person nr %d", i ) );
|
||||
|
||||
entityManager.persist( person );
|
||||
}
|
||||
} );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInClauseParameterPadding(EntityManagerFactoryScope scope) {
|
||||
final SQLStatementInspector statementInspector = (SQLStatementInspector) scope.getStatementInspector();
|
||||
statementInspector.clear();
|
||||
|
||||
scope.inTransaction( entityManager -> {
|
||||
entityManager.createQuery( "select p from Person p where p.id in :ids" )
|
||||
.setParameter( "ids", IntStream.range( 0, MAX_COUNT ).boxed().collect( Collectors.toList() ) )
|
||||
.getResultList();
|
||||
} );
|
||||
|
||||
StringBuilder expectedInClause = new StringBuilder();
|
||||
expectedInClause.append( "in(?" );
|
||||
for ( int i = 1; i < MAX_COUNT; i++ ) {
|
||||
expectedInClause.append( ",?" );
|
||||
}
|
||||
expectedInClause.append( ")" );
|
||||
|
||||
assertTrue( statementInspector.getSqlQueries().get( 0 ).endsWith( expectedInClause.toString() ) );
|
||||
}
|
||||
|
||||
@TestForIssue(jiraKey = "HHH-14109")
|
||||
@Test
|
||||
public void testInClauseParameterPaddingToLimit(EntityManagerFactoryScope scope) {
|
||||
final SQLStatementInspector statementInspector = (SQLStatementInspector) scope.getStatementInspector();
|
||||
statementInspector.clear();
|
||||
|
||||
scope.inTransaction(
|
||||
entityManager ->
|
||||
entityManager.createQuery(
|
||||
"select p from Person p where p.id in :ids" )
|
||||
.setParameter( "ids", IntStream.range( 0, 10 )
|
||||
.boxed()
|
||||
.collect( Collectors.toList() ) )
|
||||
.getResultList()
|
||||
);
|
||||
|
||||
StringBuilder expectedInClause = new StringBuilder();
|
||||
expectedInClause.append( "in(?" );
|
||||
for ( int i = 1; i < MAX_COUNT; i++ ) {
|
||||
expectedInClause.append( ",?" );
|
||||
}
|
||||
expectedInClause.append( ")" );
|
||||
|
||||
assertTrue( statementInspector.getSqlQueries().get( 0 ).endsWith( expectedInClause.toString() ) );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInClauseParameterSplittingAfterLimit(EntityManagerFactoryScope scope) {
|
||||
final SQLStatementInspector statementInspector = (SQLStatementInspector) scope.getStatementInspector();
|
||||
statementInspector.clear();
|
||||
|
||||
scope.inTransaction(
|
||||
entityManager ->
|
||||
entityManager.createQuery(
|
||||
"select p from Person p where p.id in :ids" )
|
||||
.setParameter( "ids", IntStream.range( 0, 16 )
|
||||
.boxed()
|
||||
.collect( Collectors.toList() ) )
|
||||
.getResultList()
|
||||
);
|
||||
|
||||
StringBuilder expectedInClause = new StringBuilder();
|
||||
expectedInClause.append( "in(?" );
|
||||
for ( int i = 1; i < MAX_COUNT; i++ ) {
|
||||
expectedInClause.append( ",?" );
|
||||
}
|
||||
expectedInClause.append( ")" );
|
||||
expectedInClause.append( " and p1_0.id in(?)" );
|
||||
|
||||
assertTrue( statementInspector.getSqlQueries().get( 0 ).endsWith( expectedInClause.toString() ) );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInClauseParameterSplittingAfterLimit2(EntityManagerFactoryScope scope) {
|
||||
final SQLStatementInspector statementInspector = (SQLStatementInspector) scope.getStatementInspector();
|
||||
statementInspector.clear();
|
||||
|
||||
scope.inTransaction(
|
||||
entityManager ->
|
||||
entityManager.createQuery(
|
||||
"select p from Person p where p.id in :ids" )
|
||||
.setParameter( "ids", IntStream.range( 0, 18 )
|
||||
.boxed()
|
||||
.collect( Collectors.toList() ) )
|
||||
.getResultList()
|
||||
);
|
||||
|
||||
StringBuilder expectedInClause = new StringBuilder();
|
||||
expectedInClause.append( "in(?" );
|
||||
for ( int i = 1; i < MAX_COUNT; i++ ) {
|
||||
expectedInClause.append( ",?" );
|
||||
}
|
||||
expectedInClause.append( ")" );
|
||||
expectedInClause.append( " and p1_0.id in(?,?,?,?)" );
|
||||
|
||||
assertTrue( statementInspector.getSqlQueries().get( 0 ).endsWith( expectedInClause.toString() ) );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInClauseParameterSplittingAfterLimit3(EntityManagerFactoryScope scope) {
|
||||
final SQLStatementInspector statementInspector = (SQLStatementInspector) scope.getStatementInspector();
|
||||
statementInspector.clear();
|
||||
|
||||
scope.inTransaction(
|
||||
entityManager ->
|
||||
entityManager.createQuery(
|
||||
"select p from Person p where p.id in :ids" )
|
||||
.setParameter( "ids", IntStream.range( 0, 33 )
|
||||
.boxed()
|
||||
.collect( Collectors.toList() ) )
|
||||
.getResultList()
|
||||
);
|
||||
|
||||
StringBuilder expectedInClause = new StringBuilder();
|
||||
expectedInClause.append( "in(?" );
|
||||
for ( int i = 1; i < MAX_COUNT; i++ ) {
|
||||
expectedInClause.append( ",?" );
|
||||
}
|
||||
expectedInClause.append( ")" );
|
||||
expectedInClause.append( " and p1_0.id in(?");
|
||||
for ( int i = 1; i < MAX_COUNT; i++ ) {
|
||||
expectedInClause.append( ",?" );
|
||||
}
|
||||
expectedInClause.append( ")" );
|
||||
expectedInClause.append( " and p1_0.id in(?,?,?,?)" );
|
||||
|
||||
|
||||
assertTrue( statementInspector.getSqlQueries().get( 0 ).endsWith( expectedInClause.toString() ) );
|
||||
}
|
||||
|
||||
@Entity(name = "Person")
|
||||
public static class Person {
|
||||
|
||||
@Id
|
||||
private Integer id;
|
||||
|
||||
private String name;
|
||||
|
||||
public Integer getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Integer id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
}
|
||||
|
||||
public static class MaxCountInExpressionH2Dialect extends H2Dialect {
|
||||
|
||||
public MaxCountInExpressionH2Dialect() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getInExpressionCountLimit() {
|
||||
return MAX_COUNT;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -1,126 +0,0 @@
|
|||
/*
|
||||
* 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;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.criteria.CriteriaBuilder;
|
||||
import jakarta.persistence.criteria.CriteriaQuery;
|
||||
import jakarta.persistence.criteria.ParameterExpression;
|
||||
import jakarta.persistence.criteria.Root;
|
||||
|
||||
import org.hibernate.cfg.AvailableSettings;
|
||||
import org.hibernate.orm.test.jpa.BaseEntityManagerFunctionalTestCase;
|
||||
|
||||
import org.hibernate.testing.TestForIssue;
|
||||
import org.hibernate.testing.jdbc.SQLStatementInterceptor;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.hibernate.testing.transaction.TransactionUtil.doInJPA;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
/**
|
||||
* @author Vlad Mihalcea
|
||||
*/
|
||||
@TestForIssue(jiraKey = "HHH-13108")
|
||||
public class InClauseParameterPaddingCriteriaTest extends BaseEntityManagerFunctionalTestCase {
|
||||
|
||||
private SQLStatementInterceptor sqlStatementInterceptor;
|
||||
|
||||
@Override
|
||||
protected void addConfigOptions(Map options) {
|
||||
sqlStatementInterceptor = new SQLStatementInterceptor( options );
|
||||
options.put( AvailableSettings.USE_SQL_COMMENTS, Boolean.TRUE.toString() );
|
||||
options.put( AvailableSettings.IN_CLAUSE_PARAMETER_PADDING, Boolean.TRUE.toString() );
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class[] getAnnotatedClasses() {
|
||||
return new Class[] {
|
||||
Document.class
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void afterEntityManagerFactoryBuilt() {
|
||||
doInJPA( this::entityManagerFactory, entityManager -> {
|
||||
Document document = new Document();
|
||||
document.setName( "A" );
|
||||
entityManager.persist( document );
|
||||
} );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInClauseParameterPadding() {
|
||||
sqlStatementInterceptor.clear();
|
||||
|
||||
doInJPA( this::entityManagerFactory, entityManager -> {
|
||||
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
|
||||
CriteriaQuery<Long> query = cb.createQuery( Long.class );
|
||||
Root<Document> document = query.from( Document.class );
|
||||
|
||||
ParameterExpression<List> inClauseParams = cb.parameter( List.class, "ids" );
|
||||
|
||||
query
|
||||
.select( document.get( "id" ) )
|
||||
.where(
|
||||
document.get( "id" ).in( inClauseParams )
|
||||
);
|
||||
|
||||
|
||||
List<Long> ids = entityManager.createQuery( query )
|
||||
.setParameter(
|
||||
"ids",
|
||||
Arrays.asList(
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
4,
|
||||
5
|
||||
)
|
||||
)
|
||||
.getResultList();
|
||||
assertEquals( 1, ids.size() );
|
||||
} );
|
||||
|
||||
assertTrue( sqlStatementInterceptor.getSqlQueries().get( 0 ).endsWith( "in (? , ? , ? , ? , ? , ? , ? , ?)" ) );
|
||||
|
||||
}
|
||||
|
||||
@Entity(name = "Document")
|
||||
public static class Document {
|
||||
|
||||
@Id
|
||||
@GeneratedValue
|
||||
private Integer id;
|
||||
|
||||
private String name;
|
||||
|
||||
public Integer getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Integer id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -1,113 +0,0 @@
|
|||
/*
|
||||
* 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;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Map;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.Id;
|
||||
|
||||
import org.hibernate.cfg.AvailableSettings;
|
||||
import org.hibernate.orm.test.jpa.BaseEntityManagerFunctionalTestCase;
|
||||
|
||||
import org.hibernate.testing.TestForIssue;
|
||||
import org.hibernate.testing.jdbc.SQLStatementInterceptor;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.hibernate.testing.transaction.TransactionUtil.doInJPA;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
/**
|
||||
* @author Vlad Mihalcea
|
||||
*/
|
||||
@TestForIssue( jiraKey = "HHH-12469" )
|
||||
public class InClauseParameterPaddingTest extends BaseEntityManagerFunctionalTestCase {
|
||||
|
||||
private SQLStatementInterceptor sqlStatementInterceptor;
|
||||
|
||||
@Override
|
||||
protected void addConfigOptions(Map options) {
|
||||
sqlStatementInterceptor = new SQLStatementInterceptor( options );
|
||||
options.put( AvailableSettings.USE_SQL_COMMENTS, Boolean.TRUE.toString() );
|
||||
options.put( AvailableSettings.IN_CLAUSE_PARAMETER_PADDING, Boolean.TRUE.toString() );
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class[] getAnnotatedClasses() {
|
||||
return new Class[] {
|
||||
Person.class
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void afterEntityManagerFactoryBuilt() {
|
||||
doInJPA( this::entityManagerFactory, entityManager -> {
|
||||
for ( int i = 1; i < 10; i++ ) {
|
||||
Person person = new Person();
|
||||
person.setId( i );
|
||||
person.setName( String.format( "Person nr %d", i ) );
|
||||
|
||||
entityManager.persist( person );
|
||||
}
|
||||
} );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInClauseParameterPadding() {
|
||||
validateInClauseParameterPadding( "in (?)", 1 );
|
||||
validateInClauseParameterPadding( "in (? , ?)", 1, 2 );
|
||||
validateInClauseParameterPadding( "in (? , ? , ? , ?)", 1, 2, 3 );
|
||||
validateInClauseParameterPadding( "in (? , ? , ? , ?)", 1, 2, 3, 4 );
|
||||
validateInClauseParameterPadding( "in (? , ? , ? , ? , ? , ? , ? , ?)", 1, 2, 3, 4, 5 );
|
||||
validateInClauseParameterPadding( "in (? , ? , ? , ? , ? , ? , ? , ?)", 1, 2, 3, 4, 5, 6 );
|
||||
validateInClauseParameterPadding( "in (? , ? , ? , ? , ? , ? , ? , ?)", 1, 2, 3, 4, 5, 6, 7 );
|
||||
validateInClauseParameterPadding( "in (? , ? , ? , ? , ? , ? , ? , ?)", 1, 2, 3, 4, 5, 6, 7, 8 );
|
||||
validateInClauseParameterPadding( "in (? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ?)", 1, 2, 3, 4, 5, 6, 7, 8, 9 );
|
||||
validateInClauseParameterPadding( "in (? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ?)", 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 );
|
||||
}
|
||||
|
||||
private void validateInClauseParameterPadding(String expectedInClause, Integer... ids) {
|
||||
sqlStatementInterceptor.clear();
|
||||
|
||||
doInJPA( this::entityManagerFactory, entityManager -> {
|
||||
return entityManager.createQuery(
|
||||
"select p " +
|
||||
"from Person p " +
|
||||
"where p.id in :ids" )
|
||||
.setParameter( "ids", Arrays.asList(ids) )
|
||||
.getResultList();
|
||||
} );
|
||||
|
||||
assertTrue(sqlStatementInterceptor.getSqlQueries().get( 0 ).endsWith( expectedInClause ));
|
||||
}
|
||||
|
||||
@Entity(name = "Person")
|
||||
public static class Person {
|
||||
|
||||
@Id
|
||||
private Integer id;
|
||||
|
||||
private String name;
|
||||
|
||||
public Integer getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Integer id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -1,147 +0,0 @@
|
|||
/*
|
||||
* 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;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.IntStream;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.Id;
|
||||
|
||||
import org.hibernate.cfg.AvailableSettings;
|
||||
import org.hibernate.dialect.Dialect;
|
||||
import org.hibernate.dialect.H2Dialect;
|
||||
import org.hibernate.orm.test.jpa.BaseEntityManagerFunctionalTestCase;
|
||||
|
||||
import org.hibernate.testing.RequiresDialect;
|
||||
import org.hibernate.testing.TestForIssue;
|
||||
import org.hibernate.testing.jdbc.SQLStatementInterceptor;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.hibernate.testing.transaction.TransactionUtil.doInJPA;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
/**
|
||||
* @author Vlad Mihalcea
|
||||
*/
|
||||
@TestForIssue( jiraKey = "HHH-12469" )
|
||||
@RequiresDialect(H2Dialect.class)
|
||||
public class MaxInExpressionParameterPaddingTest extends BaseEntityManagerFunctionalTestCase {
|
||||
|
||||
public static final int MAX_COUNT = 15;
|
||||
|
||||
private SQLStatementInterceptor sqlStatementInterceptor;
|
||||
|
||||
@Override
|
||||
protected void addConfigOptions(Map options) {
|
||||
sqlStatementInterceptor = new SQLStatementInterceptor( options );
|
||||
options.put( AvailableSettings.USE_SQL_COMMENTS, Boolean.TRUE.toString() );
|
||||
options.put( AvailableSettings.IN_CLAUSE_PARAMETER_PADDING, Boolean.TRUE.toString() );
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class[] getAnnotatedClasses() {
|
||||
return new Class[] {
|
||||
Person.class
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Dialect getDialect() {
|
||||
return new MaxCountInExpressionH2Dialect();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void afterEntityManagerFactoryBuilt() {
|
||||
doInJPA( this::entityManagerFactory, entityManager -> {
|
||||
for ( int i = 0; i < MAX_COUNT; i++ ) {
|
||||
Person person = new Person();
|
||||
person.setId( i );
|
||||
person.setName( String.format( "Person nr %d", i ) );
|
||||
|
||||
entityManager.persist( person );
|
||||
}
|
||||
} );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInClauseParameterPadding() {
|
||||
sqlStatementInterceptor.clear();
|
||||
|
||||
doInJPA( this::entityManagerFactory, entityManager -> {
|
||||
return entityManager.createQuery( "select p from Person p where p.id in :ids" )
|
||||
.setParameter( "ids", IntStream.range( 0, MAX_COUNT ).boxed().collect( Collectors.toList() ) )
|
||||
.getResultList();
|
||||
} );
|
||||
|
||||
StringBuilder expectedInClause = new StringBuilder();
|
||||
expectedInClause.append( "in (?" );
|
||||
for ( int i = 1; i < MAX_COUNT; i++ ) {
|
||||
expectedInClause.append( " , ?" );
|
||||
}
|
||||
expectedInClause.append( ")" );
|
||||
|
||||
assertTrue( sqlStatementInterceptor.getSqlQueries().get( 0 ).endsWith( expectedInClause.toString() ) );
|
||||
}
|
||||
|
||||
@TestForIssue( jiraKey = "HHH-14109" )
|
||||
@Test
|
||||
public void testInClauseParameterPaddingToLimit() {
|
||||
sqlStatementInterceptor.clear();
|
||||
|
||||
doInJPA( this::entityManagerFactory, entityManager -> {
|
||||
return entityManager.createQuery(
|
||||
"select p " +
|
||||
"from Person p " +
|
||||
"where p.id in :ids" )
|
||||
.setParameter( "ids", IntStream.range( 0, 10 ).boxed().collect(Collectors.toList()) )
|
||||
.getResultList();
|
||||
} );
|
||||
|
||||
StringBuilder expectedInClause = new StringBuilder();
|
||||
expectedInClause.append( "in (?" );
|
||||
for ( int i = 1; i < MAX_COUNT; i++ ) {
|
||||
expectedInClause.append( " , ?" );
|
||||
}
|
||||
expectedInClause.append( ")" );
|
||||
|
||||
assertTrue(sqlStatementInterceptor.getSqlQueries().get( 0 ).endsWith( expectedInClause.toString() ));
|
||||
}
|
||||
|
||||
@Entity(name = "Person")
|
||||
public static class Person {
|
||||
|
||||
@Id
|
||||
private Integer id;
|
||||
|
||||
private String name;
|
||||
|
||||
public Integer getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Integer id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
}
|
||||
|
||||
public static class MaxCountInExpressionH2Dialect extends H2Dialect {
|
||||
@Override
|
||||
public int getInExpressionCountLimit() {
|
||||
return MAX_COUNT;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -26,6 +26,7 @@ import org.hibernate.jpa.boot.spi.Bootstrap;
|
|||
import org.hibernate.jpa.boot.spi.EntityManagerFactoryBuilder;
|
||||
import org.hibernate.query.sqm.mutation.internal.idtable.GlobalTemporaryTableStrategy;
|
||||
import org.hibernate.query.sqm.mutation.internal.idtable.LocalTemporaryTableStrategy;
|
||||
import org.hibernate.resource.jdbc.spi.StatementInspector;
|
||||
import org.hibernate.tool.schema.Action;
|
||||
import org.hibernate.tool.schema.spi.SchemaManagementToolCoordinator;
|
||||
import org.hibernate.tool.schema.spi.SchemaManagementToolCoordinator.ActionGrouping;
|
||||
|
@ -99,6 +100,9 @@ public class EntityManagerFactoryExtension
|
|||
pui.getProperties().put( AvailableSettings.JPA_ID_GENERATOR_GLOBAL_SCOPE_COMPLIANCE, emfAnn.generatorScopeComplianceEnabled() );
|
||||
pui.getProperties().put( AvailableSettings.JPA_ORDER_BY_MAPPING_COMPLIANCE, emfAnn.orderByMappingComplianceEnabled() );
|
||||
pui.getProperties().put( AvailableSettings.JPA_LOAD_BY_ID_COMPLIANCE, emfAnn.loadByIdComplianceEnabled() );
|
||||
if ( !emfAnn.statementInspectorClass().equals( StatementInspector.class ) ) {
|
||||
pui.getProperties().put( AvailableSettings.STATEMENT_INSPECTOR, emfAnn.statementInspectorClass() );
|
||||
}
|
||||
|
||||
final Setting[] properties = emfAnn.properties();
|
||||
for ( int i = 0; i < properties.length; i++ ) {
|
||||
|
|
|
@ -28,4 +28,6 @@ public interface EntityManagerFactoryScope {
|
|||
<T> T fromEntityManager(Function<EntityManager, T> action);
|
||||
<T> T fromTransaction(Function<EntityManager, T> action);
|
||||
<T> T fromTransaction(EntityManager entityManager, Function<EntityManager, T> action);
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@ import jakarta.persistence.ValidationMode;
|
|||
import jakarta.persistence.spi.PersistenceUnitTransactionType;
|
||||
|
||||
import org.hibernate.jpa.spi.JpaCompliance;
|
||||
import org.hibernate.resource.jdbc.spi.StatementInspector;
|
||||
|
||||
import org.hibernate.testing.orm.domain.DomainModelDescriptor;
|
||||
import org.hibernate.testing.orm.domain.StandardDomainModel;
|
||||
|
@ -108,6 +109,8 @@ public @interface Jpa {
|
|||
*/
|
||||
boolean loadByIdComplianceEnabled() default false;
|
||||
|
||||
Class<? extends StatementInspector> statementInspectorClass() default StatementInspector.class;
|
||||
|
||||
boolean excludeUnlistedClasses() default false;
|
||||
|
||||
StandardDomainModel[] standardModels() default {};
|
||||
|
|
Loading…
Reference in New Issue