HHH-16984 Disable use of arrays for batch and multi-loader on H2

This commit is contained in:
Christian Beikov 2023-07-24 13:03:24 +02:00 committed by Sanne Grinovero
parent 7eba1b4483
commit 949397f1bd
13 changed files with 58 additions and 28 deletions

View File

@ -207,6 +207,12 @@ public class H2LegacyDialect extends Dialect {
return getVersion().isSameOrAfter( 2 );
}
@Override
public boolean useArrayForMultiValuedParameters() {
// Performance is worse than the in-predicate version
return false;
}
@Override
protected String columnType(int sqlTypeCode) {
switch ( sqlTypeCode ) {

View File

@ -4151,6 +4151,17 @@ public abstract class Dialect implements ConversionContext, TypeContributor, Fun
return false;
}
/**
* Does this database prefer to use array types for multi-valued parameters.
*
* @return boolean
*
* @since 6.2.8
*/
public boolean useArrayForMultiValuedParameters() {
return supportsStandardArrays() && getPreferredSqlTypeCodeForArray() == SqlTypes.ARRAY;
}
/**
* The SQL type name for the array type with elements of the given type name.
* <p>

View File

@ -193,6 +193,12 @@ public class H2Dialect extends Dialect {
return getVersion().isSameOrAfter( 2 );
}
@Override
public boolean useArrayForMultiValuedParameters() {
// Performance is worse than the in-predicate version
return false;
}
@Override
protected String columnType(int sqlTypeCode) {
switch ( sqlTypeCode ) {

View File

@ -12,7 +12,6 @@ import org.hibernate.dialect.Dialect;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.metamodel.mapping.JdbcMapping;
import org.hibernate.type.BasicType;
import org.hibernate.type.SqlTypes;
import org.hibernate.type.descriptor.java.BasicPluralJavaType;
import org.hibernate.type.descriptor.java.JavaType;
import org.hibernate.type.descriptor.java.spi.JavaTypeRegistry;
@ -26,8 +25,7 @@ public class MultiKeyLoadHelper {
}
public static boolean supportsSqlArrayType(Dialect dialect) {
return dialect.supportsStandardArrays()
&& dialect.getPreferredSqlTypeCodeForArray() == SqlTypes.ARRAY;
return dialect.useArrayForMultiValuedParameters();
}
public static JdbcMapping resolveArrayJdbcMapping(

View File

@ -62,9 +62,7 @@ public class StandardBatchLoaderFactory implements BatchLoaderFactory {
SessionFactoryImplementor factory) {
final Dialect dialect = factory.getJdbcServices().getDialect();
final int columnCount = attributeMapping.getKeyDescriptor().getJdbcTypeCount();
if ( columnCount == 1
&& dialect.supportsStandardArrays()
&& dialect.getPreferredSqlTypeCodeForArray() == SqlTypes.ARRAY ) {
if ( columnCount == 1 && MultiKeyLoadHelper.supportsSqlArrayType( dialect ) ) {
// we can use a single ARRAY parameter to send all the ids
return new CollectionBatchLoaderArrayParam( domainBatchSize, influencers, attributeMapping, factory );
}

View File

@ -119,6 +119,7 @@ public class QueryInterpretationCacheStandardImpl implements QueryInterpretation
Class<?> expectedResultType,
HqlTranslator translator) {
log.tracef( "QueryPlan#resolveHqlInterpretation( `%s` )", queryString );
final StatisticsImplementor statistics = statisticsSupplier.get();
final Object cacheKey;
if ( expectedResultType != null ) {
@ -129,30 +130,35 @@ public class QueryInterpretationCacheStandardImpl implements QueryInterpretation
}
final HqlInterpretation existing = hqlInterpretationCache.get( cacheKey );
if ( existing != null ) {
final StatisticsImplementor statistics = statisticsSupplier.get();
if ( statistics.isStatisticsEnabled() ) {
statistics.queryPlanCacheHit( queryString );
}
return existing;
}
else {
final HqlInterpretation hqlInterpretation = createHqlInterpretation(
queryString,
expectedResultType,
translator,
statisticsSupplier
);
hqlInterpretationCache.put( cacheKey, hqlInterpretation );
return hqlInterpretation;
else if ( expectedResultType != null ) {
final HqlInterpretation existingQueryOnly = hqlInterpretationCache.get( queryString );
if ( existingQueryOnly != null ) {
if ( statistics.isStatisticsEnabled() ) {
statistics.queryPlanCacheHit( queryString );
}
return existingQueryOnly;
}
}
final HqlInterpretation hqlInterpretation = createHqlInterpretation(
queryString,
expectedResultType,
translator,
statistics
);
hqlInterpretationCache.put( cacheKey, hqlInterpretation );
return hqlInterpretation;
}
protected static HqlInterpretation createHqlInterpretation(
String queryString,
Class<?> expectedResultType,
HqlTranslator translator,
Supplier<StatisticsImplementor> statisticsSupplier) {
final StatisticsImplementor statistics = statisticsSupplier.get();
StatisticsImplementor statistics) {
final boolean stats = statistics.isStatisticsEnabled();
final long startTime = ( stats ) ? System.nanoTime() : 0L;

View File

@ -103,7 +103,7 @@ public class BatchAndClassIdAndLazyCollectionTest {
}
statementInspector.assertExecutedCount( 2 );
if ( scope.getSessionFactory().getJdbcServices().getDialect().supportsStandardArrays() ) {
if ( scope.getSessionFactory().getJdbcServices().getDialect().useArrayForMultiValuedParameters() ) {
Assertions.assertThat( statementInspector.getSqlQueries().get( 0 ) ).containsOnlyOnce( "?" );
Assertions.assertThat( statementInspector.getSqlQueries().get( 1 ) ).containsOnlyOnce( "?" );
}

View File

@ -95,7 +95,7 @@ public class BatchAndClassIdTest {
statementInspector.clear();
List<Child> children = session.createQuery( "select c from Child c", Child.class ).getResultList();
statementInspector.assertExecutedCount( 3 );
if ( scope.getSessionFactory().getJdbcServices().getDialect().supportsStandardArrays() ) {
if ( scope.getSessionFactory().getJdbcServices().getDialect().useArrayForMultiValuedParameters() ) {
Assertions.assertThat( statementInspector.getSqlQueries().get( 1 ) ).containsOnlyOnce( "?" );
Assertions.assertThat( statementInspector.getSqlQueries().get( 2 ) ).containsOnlyOnce( "?" );
}

View File

@ -95,7 +95,7 @@ public class BatchAndEmbeddedIdId2Test {
statementInspector.clear();
List<Child> children = session.createQuery( "select c from Child c", Child.class ).getResultList();
statementInspector.assertExecutedCount( 3 );
if ( scope.getSessionFactory().getJdbcServices().getDialect().supportsStandardArrays() ) {
if ( scope.getSessionFactory().getJdbcServices().getDialect().useArrayForMultiValuedParameters() ) {
Assertions.assertThat( statementInspector.getSqlQueries().get( 1 ) ).containsOnlyOnce( "?" );
Assertions.assertThat( statementInspector.getSqlQueries().get( 2 ) ).containsOnlyOnce( "?" );
}

View File

@ -103,7 +103,7 @@ public class BatchAndEmbeddedIdIdAndLazyCollectionTest {
}
statementInspector.assertExecutedCount( 2 );
if ( scope.getSessionFactory().getJdbcServices().getDialect().supportsStandardArrays() ) {
if ( scope.getSessionFactory().getJdbcServices().getDialect().useArrayForMultiValuedParameters() ) {
Assertions.assertThat( statementInspector.getSqlQueries().get( 0 ) ).containsOnlyOnce( "?" );
Assertions.assertThat( statementInspector.getSqlQueries().get( 1 ) ).containsOnlyOnce( "?" );
}

View File

@ -93,7 +93,7 @@ public class BatchAndEmbeddedIdIdTest {
statementInspector.clear();
List<Child> children = session.createQuery( "select c from Child c", Child.class ).getResultList();
statementInspector.assertExecutedCount( 3 );
if ( scope.getSessionFactory().getJdbcServices().getDialect().supportsStandardArrays() ) {
if ( scope.getSessionFactory().getJdbcServices().getDialect().useArrayForMultiValuedParameters() ) {
assertThat( statementInspector.getSqlQueries().get( 1 ) ).containsOnlyOnce( "?" );
assertThat( statementInspector.getSqlQueries().get( 2 ) ).containsOnlyOnce( "?" );
}

View File

@ -126,7 +126,7 @@ public class NestedLazyManyToOneTest {
// 1 for Entity1, 1 for Entity2, 1 for Entity3
statementInspector.assertExecutedCount( 3 );
statementInspector.assertNumberOfOccurrenceInQueryNoSpace( 1, QUESTION_MARK, 1 );
if ( MultiKeyLoadHelper.supportsSqlArrayType( scope.getSessionFactory().getJdbcServices().getDialect() ) ) {
if ( scope.getSessionFactory().getJdbcServices().getDialect().useArrayForMultiValuedParameters() ) {
assertThat( StringHelper.count( statementInspector.getSqlQueries().get( 2 ), '?' ) ).isEqualTo( 1 );
}
else {
@ -156,7 +156,7 @@ public class NestedLazyManyToOneTest {
// 1 for Entity1, 1 for Entity2, 2 for Entity3
assertThat( statementInspector.getSqlQueries() ).hasSize( 4 );
assertThat( StringHelper.count( statementInspector.getSqlQueries().get( 1 ), '?' ) ).isEqualTo( 1 );
if ( MultiKeyLoadHelper.supportsSqlArrayType( scope.getSessionFactory().getJdbcServices().getDialect() ) ) {
if ( scope.getSessionFactory().getJdbcServices().getDialect().useArrayForMultiValuedParameters() ) {
assertThat( StringHelper.count( statementInspector.getSqlQueries().get( 2 ), '?' ) ).isEqualTo( 1 );
assertThat( StringHelper.count( statementInspector.getSqlQueries().get( 3 ), '?' ) ).isEqualTo( 1 );
}

View File

@ -109,6 +109,7 @@ public class MultiLoadSubSelectCollectionDialectWithLimitTest {
@TestForIssue(jiraKey = "HHH-12740")
public void testSubselect(SessionFactoryScope scope) {
final SQLStatementInspector statementInspector = scope.getCollectingStatementInspector();
final Dialect dialect = scope.getSessionFactory().getFastSessionServices().jdbcServices.getDialect();
statementInspector.clear();
scope.inTransaction(
@ -117,7 +118,12 @@ public class MultiLoadSubSelectCollectionDialectWithLimitTest {
assertEquals( 56, list.size() );
// None of the collections should be loaded yet
assertThat( statementInspector.getSqlQueries() ).hasSize( 1 );
if ( dialect.useArrayForMultiValuedParameters() ) {
assertThat( statementInspector.getSqlQueries() ).hasSize( 1 );
}
else {
assertThat( statementInspector.getSqlQueries() ).hasSize( 2 );
}
for ( Parent p : list ) {
assertFalse( Hibernate.isInitialized( p.children ) );
}
@ -128,8 +134,7 @@ public class MultiLoadSubSelectCollectionDialectWithLimitTest {
Hibernate.initialize( list.get( 0 ).children );
// exactly how depends on whether the Dialect supports use of SQL ARRAY
final Dialect dialect = scope.getSessionFactory().getFastSessionServices().jdbcServices.getDialect();
if ( MultiKeyLoadHelper.supportsSqlArrayType( dialect ) ) {
if ( dialect.useArrayForMultiValuedParameters() ) {
assertThat( Hibernate.isInitialized( list.get( 0 ).children ) ).isTrue();
assertThat( Hibernate.isInitialized( list.get( 50 ).children ) ).isTrue();
assertThat( Hibernate.isInitialized( list.get( 52 ).children ) ).isTrue();