From b276128f56ba6ac16f50a15d69ab326de0450d54 Mon Sep 17 00:00:00 2001 From: Steve Ebersole Date: Mon, 13 Mar 2023 13:58:25 -0500 Subject: [PATCH] HHH-16284 - Rename JdbcParameterRender to ParameterMarkerStrategy --- gradle/databases.gradle | 5 +- .../org/hibernate/cfg/AvailableSettings.java | 13 +- .../java/org/hibernate/dialect/Dialect.java | 15 +- .../dialect/DialectDelegateWrapper.java | 6 +- .../java/org/hibernate/dialect/H2Dialect.java | 12 +- .../hibernate/dialect/PostgreSQLDialect.java | 12 +- .../entity/AbstractEntityPersister.java | 2 +- .../NativeNonSelectQueryPlanImpl.java | 4 +- .../internal/NativeSelectQueryPlanImpl.java | 15 +- .../service/StandardServiceInitiators.java | 6 +- .../hibernate/sql/ComparisonRestriction.java | 24 +- .../main/java/org/hibernate/sql/Delete.java | 70 ++--- .../main/java/org/hibernate/sql/Insert.java | 17 +- .../hibernate/sql/NullnessRestriction.java | 16 +- .../java/org/hibernate/sql/SimpleSelect.java | 16 +- .../main/java/org/hibernate/sql/Update.java | 31 +- .../JdbcParameterRendererStandard.java | 25 -- ... => ParameterMarkerStrategyInitiator.java} | 22 +- .../ParameterMarkerStrategyStandard.java | 31 ++ .../sql/ast/spi/AbstractSqlAstTranslator.java | 32 +- .../sql/ast/spi/JdbcParameterRenderer.java | 39 --- .../sql/ast/spi/ParameterMarkerStrategy.java | 33 +++ .../tree/expression/LiteralAsParameter.java | 7 +- .../internal/JdbcParameterBindingsImpl.java | 94 +++++- .../sql/exec/spi/JdbcParameterBindings.java | 125 ++------ .../collection/list/PersistentListTest.java | 3 +- .../sql/ast/JdbcParameterRendererTests.java | 163 ----------- .../sql/ast/ParameterMarkerStrategyTests.java | 276 ++++++++++++++++++ .../internal/ValidityAuditStrategy.java | 9 +- 29 files changed, 639 insertions(+), 484 deletions(-) delete mode 100644 hibernate-core/src/main/java/org/hibernate/sql/ast/internal/JdbcParameterRendererStandard.java rename hibernate-core/src/main/java/org/hibernate/sql/ast/internal/{JdbcParameterRendererInitiator.java => ParameterMarkerStrategyInitiator.java} (53%) create mode 100644 hibernate-core/src/main/java/org/hibernate/sql/ast/internal/ParameterMarkerStrategyStandard.java delete mode 100644 hibernate-core/src/main/java/org/hibernate/sql/ast/spi/JdbcParameterRenderer.java create mode 100644 hibernate-core/src/main/java/org/hibernate/sql/ast/spi/ParameterMarkerStrategy.java delete mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/sql/ast/JdbcParameterRendererTests.java create mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/sql/ast/ParameterMarkerStrategyTests.java diff --git a/gradle/databases.gradle b/gradle/databases.gradle index 0f14b01bc0..6e52382caf 100644 --- a/gradle/databases.gradle +++ b/gradle/databases.gradle @@ -19,7 +19,8 @@ ext { 'jdbc.user' : 'sa', 'jdbc.pass' : '', 'jdbc.url' : 'jdbc:h2:mem:db1;DB_CLOSE_DELAY=-1;LOCK_TIMEOUT=10000', - 'connection.init_sql' : '' + 'connection.init_sql' : '', + 'hibernate.dialect.native_param_markers' : 'true' ], hsqldb : [ 'db.dialect' : 'org.hibernate.dialect.HSQLDialect', @@ -250,4 +251,4 @@ if ( processTestResourcesTask != null ) { // processTestResourcesTask.inputs.property( "gradle.libs.versions.hsqldb", project.getProperty( "gradle.libs.versions.hsqldb", "2.7.1" ) ) // processTestResourcesTask.inputs.property( "gradle.libs.versions.derby", project.getProperty( "gradle.libs.versions.derby", "10.15.2.0" ) ) processTestResourcesTask.filter( ReplaceTokens, tokens: dbBundle[db] ) -} \ No newline at end of file +} diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/AvailableSettings.java b/hibernate-core/src/main/java/org/hibernate/cfg/AvailableSettings.java index a75fff56ea..e337971705 100644 --- a/hibernate-core/src/main/java/org/hibernate/cfg/AvailableSettings.java +++ b/hibernate-core/src/main/java/org/hibernate/cfg/AvailableSettings.java @@ -29,6 +29,7 @@ import org.hibernate.query.sqm.mutation.internal.temptable.LocalTemporaryTableSt import org.hibernate.query.sqm.mutation.internal.temptable.PersistentTableStrategy; import org.hibernate.resource.jdbc.spi.PhysicalConnectionHandlingMode; import org.hibernate.resource.jdbc.spi.StatementInspector; +import org.hibernate.sql.ast.spi.ParameterMarkerStrategy; import jakarta.persistence.criteria.CriteriaDelete; import jakarta.persistence.criteria.CriteriaQuery; @@ -2789,13 +2790,15 @@ public interface AvailableSettings { String TIMEZONE_DEFAULT_STORAGE = "hibernate.timezone.default_storage"; /** - * Controls whether to use JDBC parameter markers (`?`) or dialect native markers. + * Controls whether to use JDBC markers (`?`) or dialect native markers for parameters + * within {@linkplain java.sql.PreparedStatement preparable} SQL statements. * - * @implNote By default ({@code true}), dialect native markers are used, if any; disable - * ({@code false}) to use the standard JDBC parameter markers (`?`) instead + * @implNote {@code False} by default, indicating standard JDBC parameter markers (`?`) + * are used. Set to {@code true} to use the Dialect's native markers, if any. For + * Dialects without native markers, the standard JDBC strategy is used. * - * @see org.hibernate.sql.ast.spi.JdbcParameterRenderer - * @see org.hibernate.dialect.Dialect#getNativeParameterRenderer() + * @see ParameterMarkerStrategy + * @see org.hibernate.dialect.Dialect#getNativeParameterMarkerStrategy() * * @since 6.2 */ diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java index 99cbcd50bf..c1d25a7384 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java @@ -144,8 +144,8 @@ import org.hibernate.service.spi.ServiceRegistryImplementor; import org.hibernate.sql.ForUpdateFragment; import org.hibernate.sql.ast.SqlAstNodeRenderingMode; import org.hibernate.sql.ast.SqlAstTranslatorFactory; -import org.hibernate.sql.ast.internal.JdbcParameterRendererStandard; -import org.hibernate.sql.ast.spi.JdbcParameterRenderer; +import org.hibernate.sql.ast.internal.ParameterMarkerStrategyStandard; +import org.hibernate.sql.ast.spi.ParameterMarkerStrategy; import org.hibernate.sql.ast.spi.SqlAppender; import org.hibernate.sql.ast.spi.StringBuilderSqlAppender; import org.hibernate.sql.model.MutationOperation; @@ -4815,10 +4815,15 @@ public abstract class Dialect implements ConversionContext, TypeContributor, Fun } /** - * Support for native parameter rendering + * Support for native parameter markers. + *

+ * This is generally dependent on both the database and the driver. + * + * @return May return {@code null} to indicate that the JDBC + * {@linkplain ParameterMarkerStrategyStandard standard} strategy should be used */ - public JdbcParameterRenderer getNativeParameterRenderer() { - return JdbcParameterRendererStandard.INSTANCE; + public ParameterMarkerStrategy getNativeParameterMarkerStrategy() { + return null; } /** diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/DialectDelegateWrapper.java b/hibernate-core/src/main/java/org/hibernate/dialect/DialectDelegateWrapper.java index 54dc66b502..b4c2d0a8c1 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/DialectDelegateWrapper.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/DialectDelegateWrapper.java @@ -75,7 +75,7 @@ import org.hibernate.query.sqm.sql.SqmTranslatorFactory; import org.hibernate.service.ServiceRegistry; import org.hibernate.service.spi.ServiceRegistryImplementor; import org.hibernate.sql.ast.SqlAstTranslatorFactory; -import org.hibernate.sql.ast.spi.JdbcParameterRenderer; +import org.hibernate.sql.ast.spi.ParameterMarkerStrategy; import org.hibernate.sql.ast.spi.SqlAppender; import org.hibernate.sql.model.MutationOperation; import org.hibernate.sql.model.internal.OptionalTableUpdate; @@ -193,8 +193,8 @@ public class DialectDelegateWrapper extends Dialect { } @Override - public JdbcParameterRenderer getNativeParameterRenderer() { - return wrapped.getNativeParameterRenderer(); + public ParameterMarkerStrategy getNativeParameterMarkerStrategy() { + return wrapped.getNativeParameterMarkerStrategy(); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/H2Dialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/H2Dialect.java index eda131cad9..9ba5b556d0 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/H2Dialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/H2Dialect.java @@ -57,7 +57,7 @@ import org.hibernate.service.ServiceRegistry; import org.hibernate.sql.ast.SqlAstNodeRenderingMode; import org.hibernate.sql.ast.SqlAstTranslator; import org.hibernate.sql.ast.SqlAstTranslatorFactory; -import org.hibernate.sql.ast.spi.JdbcParameterRenderer; +import org.hibernate.sql.ast.spi.ParameterMarkerStrategy; import org.hibernate.sql.ast.spi.SqlAppender; import org.hibernate.sql.ast.spi.StandardSqlAstTranslatorFactory; import org.hibernate.sql.ast.tree.Statement; @@ -910,18 +910,18 @@ public class H2Dialect extends Dialect { } @Override - public JdbcParameterRenderer getNativeParameterRenderer() { - return OrdinalParameterRenderer.INSTANCE; + public ParameterMarkerStrategy getNativeParameterMarkerStrategy() { + return OrdinalParameterMarkerStrategy.INSTANCE; } - public static class OrdinalParameterRenderer implements JdbcParameterRenderer { + public static class OrdinalParameterMarkerStrategy implements ParameterMarkerStrategy { /** * Singleton access */ - public static final OrdinalParameterRenderer INSTANCE = new OrdinalParameterRenderer(); + public static final OrdinalParameterMarkerStrategy INSTANCE = new OrdinalParameterMarkerStrategy(); @Override - public String renderJdbcParameter(int position, JdbcType jdbcType) { + public String createMarker(int position, JdbcType jdbcType) { return "?" + position; } } diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/PostgreSQLDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/PostgreSQLDialect.java index d62c9197f4..aa264980fc 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/PostgreSQLDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/PostgreSQLDialect.java @@ -66,7 +66,7 @@ import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy; import org.hibernate.service.ServiceRegistry; import org.hibernate.sql.ast.SqlAstTranslator; import org.hibernate.sql.ast.SqlAstTranslatorFactory; -import org.hibernate.sql.ast.spi.JdbcParameterRenderer; +import org.hibernate.sql.ast.spi.ParameterMarkerStrategy; import org.hibernate.sql.ast.spi.SqlAppender; import org.hibernate.sql.ast.spi.StandardSqlAstTranslatorFactory; import org.hibernate.sql.ast.tree.Statement; @@ -145,7 +145,7 @@ public class PostgreSQLDialect extends Dialect { protected final PostgreSQLDriverKind driverKind; private final OptionalTableUpdateStrategy optionalTableUpdateStrategy; - private final JdbcParameterRenderer parameterRenderer; + private final ParameterMarkerStrategy parameterRenderer; public PostgreSQLDialect() { this( MINIMUM_VERSION ); @@ -166,7 +166,7 @@ public class PostgreSQLDialect extends Dialect { this.optionalTableUpdateStrategy = determineOptionalTableUpdateStrategy( version ); this.parameterRenderer = driverKind == PostgreSQLDriverKind.VERT_X ? NativeParameterMarkers.INSTANCE - : super.getNativeParameterRenderer(); + : super.getNativeParameterMarkerStrategy(); } private static OptionalTableUpdateStrategy determineOptionalTableUpdateStrategy(DatabaseVersion version) { @@ -1433,18 +1433,18 @@ public class PostgreSQLDialect extends Dialect { } @Override - public JdbcParameterRenderer getNativeParameterRenderer() { + public ParameterMarkerStrategy getNativeParameterMarkerStrategy() { return parameterRenderer; } - private static class NativeParameterMarkers implements JdbcParameterRenderer { + private static class NativeParameterMarkers implements ParameterMarkerStrategy { /** * Singleton access */ public static final NativeParameterMarkers INSTANCE = new NativeParameterMarkers(); @Override - public String renderJdbcParameter(int position, JdbcType jdbcType) { + public String createMarker(int position, JdbcType jdbcType) { return "$" + position; } } diff --git a/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java b/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java index 171c8b1c0a..fa839b62e2 100644 --- a/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java +++ b/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java @@ -6129,7 +6129,7 @@ public abstract class AbstractEntityPersister delete.addColumnRestriction( propertyColumnNames[k] ); } else { - delete.addColumnNullnessRestriction( propertyColumnNames[k] ); + delete.addColumnIsNullRestriction( propertyColumnNames[k] ); } } } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sql/internal/NativeNonSelectQueryPlanImpl.java b/hibernate-core/src/main/java/org/hibernate/query/sql/internal/NativeNonSelectQueryPlanImpl.java index 0491526ffd..d27e7effad 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sql/internal/NativeNonSelectQueryPlanImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sql/internal/NativeNonSelectQueryPlanImpl.java @@ -57,9 +57,7 @@ public class NativeNonSelectQueryPlanImpl implements NonSelectQueryPlan { } else { jdbcParameterBinders = new ArrayList<>( parameterList.size() ); - jdbcParameterBindings = new JdbcParameterBindingsImpl( parameterList.size() ); - - jdbcParameterBindings.registerNativeQueryParameters( + jdbcParameterBindings = new JdbcParameterBindingsImpl( queryParameterBindings, parameterList, jdbcParameterBinders, diff --git a/hibernate-core/src/main/java/org/hibernate/query/sql/internal/NativeSelectQueryPlanImpl.java b/hibernate-core/src/main/java/org/hibernate/query/sql/internal/NativeSelectQueryPlanImpl.java index 2a2a5905ab..1218212117 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sql/internal/NativeSelectQueryPlanImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sql/internal/NativeSelectQueryPlanImpl.java @@ -79,9 +79,7 @@ public class NativeSelectQueryPlanImpl implements NativeSelectQueryPlan { } else { jdbcParameterBinders = new ArrayList<>( parameterList.size() ); - jdbcParameterBindings = new JdbcParameterBindingsImpl( parameterList.size() ); - - jdbcParameterBindings.registerNativeQueryParameters( + jdbcParameterBindings = new JdbcParameterBindingsImpl( queryParameterBindings, parameterList, jdbcParameterBinders, @@ -95,8 +93,7 @@ public class NativeSelectQueryPlanImpl implements NativeSelectQueryPlan { sql, jdbcParameterBinders, resultSetMapping, - affectedTableNames, - Collections.emptySet() + affectedTableNames ); final SharedSessionContractImplementor session = executionContext.getSession(); @@ -116,6 +113,7 @@ public class NativeSelectQueryPlanImpl implements NativeSelectQueryPlan { @Override public ScrollableResultsImplementor performScroll(ScrollMode scrollMode, DomainQueryExecutionContext executionContext) { if ( executionContext.getQueryOptions().getEffectiveLimit().getMaxRowsJpa() == 0 ) { + //noinspection unchecked return EmptyScrollableResults.INSTANCE; } final List jdbcParameterBinders; @@ -128,9 +126,7 @@ public class NativeSelectQueryPlanImpl implements NativeSelectQueryPlan { } else { jdbcParameterBinders = new ArrayList<>( parameterList.size() ); - jdbcParameterBindings = new JdbcParameterBindingsImpl( parameterList.size() ); - - jdbcParameterBindings.registerNativeQueryParameters( + jdbcParameterBindings = new JdbcParameterBindingsImpl( queryParameterBindings, parameterList, jdbcParameterBinders, @@ -142,8 +138,7 @@ public class NativeSelectQueryPlanImpl implements NativeSelectQueryPlan { sql, jdbcParameterBinders, resultSetMapping, - affectedTableNames, - Collections.emptySet() + affectedTableNames ); return executionContext.getSession().getJdbcServices().getJdbcSelectExecutor().scroll( diff --git a/hibernate-core/src/main/java/org/hibernate/service/StandardServiceInitiators.java b/hibernate-core/src/main/java/org/hibernate/service/StandardServiceInitiators.java index 102bfafb8d..690b871edd 100644 --- a/hibernate-core/src/main/java/org/hibernate/service/StandardServiceInitiators.java +++ b/hibernate-core/src/main/java/org/hibernate/service/StandardServiceInitiators.java @@ -38,7 +38,7 @@ import org.hibernate.query.sqm.mutation.internal.SqmMultiTableMutationStrategyPr import org.hibernate.resource.beans.spi.ManagedBeanRegistryInitiator; import org.hibernate.resource.transaction.internal.TransactionCoordinatorBuilderInitiator; import org.hibernate.service.internal.SessionFactoryServiceRegistryFactoryInitiator; -import org.hibernate.sql.ast.internal.JdbcParameterRendererInitiator; +import org.hibernate.sql.ast.internal.ParameterMarkerStrategyInitiator; import org.hibernate.sql.results.jdbc.internal.JdbcValuesMappingProducerProviderInitiator; import org.hibernate.tool.schema.internal.SchemaManagementToolInitiator; import org.hibernate.tool.schema.internal.script.SqlScriptExtractorInitiator; @@ -152,8 +152,8 @@ public final class StandardServiceInitiators { // SqmMultiTableMutationStrategyProvider serviceInitiators.add( SqmMultiTableMutationStrategyProviderInitiator.INSTANCE ); - // JdbcParameterRenderer - serviceInitiators.add( JdbcParameterRendererInitiator.INSTANCE ); + // ParameterMarkerStrategy + serviceInitiators.add( ParameterMarkerStrategyInitiator.INSTANCE ); serviceInitiators.trimToSize(); diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ComparisonRestriction.java b/hibernate-core/src/main/java/org/hibernate/sql/ComparisonRestriction.java index 15cb6dc828..13354d3e1e 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/ComparisonRestriction.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/ComparisonRestriction.java @@ -16,7 +16,7 @@ import org.hibernate.Internal; @Internal public class ComparisonRestriction implements Restriction { private final String lhs; - private final String operator; + private final Operator operator; private final String rhs; public ComparisonRestriction(String lhs) { @@ -24,10 +24,10 @@ public class ComparisonRestriction implements Restriction { } public ComparisonRestriction(String lhs, String rhs) { - this( lhs, "=", rhs ); + this( lhs, Operator.EQ, rhs ); } - public ComparisonRestriction(String lhs, String operator, String rhs) { + public ComparisonRestriction(String lhs, Operator operator, String rhs) { this.lhs = lhs; this.operator = operator; this.rhs = rhs; @@ -36,7 +36,7 @@ public class ComparisonRestriction implements Restriction { @Override public void render(StringBuilder sqlBuffer, RestrictionRenderingContext context) { sqlBuffer.append( lhs ); - sqlBuffer.append( operator ); + sqlBuffer.append( operator.getSqlText() ); if ( "?".equals( rhs ) ) { sqlBuffer.append( context.makeParameterMarker() ); @@ -45,4 +45,20 @@ public class ComparisonRestriction implements Restriction { sqlBuffer.append( rhs ); } } + + public enum Operator { + EQ( "=" ), + NE( "<>" ) + ; + + private final String sqlText; + + Operator(String sqlText) { + this.sqlText = sqlText; + } + + public String getSqlText() { + return sqlText; + } + } } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/Delete.java b/hibernate-core/src/main/java/org/hibernate/sql/Delete.java index c67ceb846e..0b6167e4fc 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/Delete.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/Delete.java @@ -5,16 +5,14 @@ * See the lgpl.txt file in the root directory or . */ package org.hibernate.sql; + import java.util.ArrayList; -import java.util.Iterator; -import java.util.LinkedHashMap; import java.util.List; -import java.util.Map; import org.hibernate.Internal; import org.hibernate.dialect.Dialect; import org.hibernate.engine.spi.SessionFactoryImplementor; -import org.hibernate.sql.ast.spi.JdbcParameterRenderer; +import org.hibernate.sql.ast.spi.ParameterMarkerStrategy; /** * A SQL {@code DELETE} statement. @@ -22,24 +20,20 @@ import org.hibernate.sql.ast.spi.JdbcParameterRenderer; * @author Gavin King */ @Internal -public class Delete { - +public class Delete implements RestrictionRenderingContext { protected String tableName; protected String comment; + protected final List restrictions = new ArrayList<>(); - protected final List whereFragments = new ArrayList<>(); - - private final JdbcParameterRenderer jdbcParameterRenderer; - private final boolean standardParamRendering; + private final ParameterMarkerStrategy parameterMarkerStrategy; private int parameterCount; public Delete(SessionFactoryImplementor factory) { - this( factory.getServiceRegistry().getService( JdbcParameterRenderer.class ) ); + this( factory.getServiceRegistry().getService( ParameterMarkerStrategy.class ) ); } - public Delete(JdbcParameterRenderer jdbcParameterRenderer) { - this.jdbcParameterRenderer = jdbcParameterRenderer; - this.standardParamRendering = JdbcParameterRenderer.isStandardRenderer( jdbcParameterRenderer ); + public Delete(ParameterMarkerStrategy parameterMarkerStrategy) { + this.parameterMarkerStrategy = parameterMarkerStrategy; } public Delete setTableName(String tableName) { @@ -54,8 +48,7 @@ public class Delete { @SuppressWarnings("UnusedReturnValue") public Delete addColumnRestriction(String columnName) { - final String paramMarker = jdbcParameterRenderer.renderJdbcParameter( ++parameterCount, null ); - this.whereFragments.add( columnName + "=" + paramMarker ); + restrictions.add( new ComparisonRestriction( columnName ) ); return this; } @@ -71,17 +64,14 @@ public class Delete { } @SuppressWarnings("UnusedReturnValue") - public Delete addColumnNullnessRestriction(String columnName) { - addColumnNullnessRestriction( columnName, false ); + public Delete addColumnIsNullRestriction(String columnName) { + restrictions.add( new NullnessRestriction( columnName ) ); return this; } @SuppressWarnings("UnusedReturnValue") - public Delete addColumnNullnessRestriction(String columnName, boolean negate) { - final String fragment = negate - ? columnName + " is not null" - : columnName + " is null"; - whereFragments.add( fragment ); + public Delete addColumnIsNotNullRestriction(String columnName) { + restrictions.add( new NullnessRestriction( columnName, false ) ); return this; } @@ -102,25 +92,29 @@ public class Delete { return buf.toString(); } - private void applyRestrictions(StringBuilder buf) { - if ( whereFragments.isEmpty() ) { - return; - } - - buf.append( " where " ); - - for ( int i = 0; i < whereFragments.size(); i++ ) { - if ( i > 0 ) { - buf.append( " and " ); - } - buf.append( whereFragments.get(i) ); - } - } - private void applyComment(StringBuilder buf) { if ( comment != null ) { buf.append( "/* " ).append( Dialect.escapeComment( comment ) ).append( " */ " ); } } + private void applyRestrictions(StringBuilder buf) { + if ( restrictions.isEmpty() ) { + return; + } + + buf.append( " where " ); + + for ( int i = 0; i < restrictions.size(); i++ ) { + if ( i > 0 ) { + buf.append( " and " ); + } + restrictions.get( i ).render( buf, this ); + } + } + + @Override + public String makeParameterMarker() { + return parameterMarkerStrategy.createMarker( ++parameterCount, null ); + } } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/Insert.java b/hibernate-core/src/main/java/org/hibernate/sql/Insert.java index 5a75f8d453..6c8ea70c8e 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/Insert.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/Insert.java @@ -16,9 +16,8 @@ import org.hibernate.dialect.Dialect; import org.hibernate.dialect.identity.IdentityColumnSupport; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.generator.OnExecutionGenerator; -import org.hibernate.sql.ast.spi.JdbcParameterRenderer; +import org.hibernate.sql.ast.spi.ParameterMarkerStrategy; -import static org.hibernate.sql.ast.spi.JdbcParameterRenderer.isStandardRenderer; /** * A SQL {@code INSERT} statement. @@ -34,21 +33,19 @@ public class Insert { protected Map columns = new LinkedHashMap<>(); private final Dialect dialect; - private final JdbcParameterRenderer jdbcParameterRenderer; - private final boolean standardParamRendering; + private final ParameterMarkerStrategy parameterMarkerStrategy; private int parameterCount; public Insert(SessionFactoryImplementor sessionFactory) { this( sessionFactory.getJdbcServices().getDialect(), - sessionFactory.getServiceRegistry().getService( JdbcParameterRenderer.class ) + sessionFactory.getServiceRegistry().getService( ParameterMarkerStrategy.class ) ); } - public Insert(Dialect dialect, JdbcParameterRenderer jdbcParameterRenderer) { + public Insert(Dialect dialect, ParameterMarkerStrategy parameterMarkerStrategy) { this.dialect = dialect; - this.jdbcParameterRenderer = jdbcParameterRenderer; - this.standardParamRendering = isStandardRenderer( jdbcParameterRenderer ); + this.parameterMarkerStrategy = parameterMarkerStrategy; } protected Dialect getDialect() { @@ -179,8 +176,8 @@ public class Insert { } private String normalizeExpressionFragment(String rhs) { - return rhs.equals( "?" ) && !standardParamRendering - ? jdbcParameterRenderer.renderJdbcParameter( ++parameterCount, null ) + return rhs.equals( "?" ) + ? parameterMarkerStrategy.createMarker( ++parameterCount, null ) : rhs; } } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/NullnessRestriction.java b/hibernate-core/src/main/java/org/hibernate/sql/NullnessRestriction.java index b607e59630..e538f5bd9d 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/NullnessRestriction.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/NullnessRestriction.java @@ -9,32 +9,32 @@ package org.hibernate.sql; import org.hibernate.Internal; /** - * Nullness restriction - is [not] null + * Nullness restriction - IS (NOT)? NULL * * @author Steve Ebersole */ @Internal public class NullnessRestriction implements Restriction { private final String columnName; - private final boolean negated; + private final boolean affirmative; public NullnessRestriction(String columnName) { - this( columnName, false ); + this( columnName, true ); } - public NullnessRestriction(String columnName, boolean negated) { + public NullnessRestriction(String columnName, boolean affirmative) { this.columnName = columnName; - this.negated = negated; + this.affirmative = affirmative; } @Override public void render(StringBuilder sqlBuffer, RestrictionRenderingContext context) { sqlBuffer.append( columnName ); - if ( negated ) { - sqlBuffer.append( " is not null" ); + if ( affirmative ) { + sqlBuffer.append( " is null" ); } else { - sqlBuffer.append( " is null" ); + sqlBuffer.append( " is not null" ); } } } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/SimpleSelect.java b/hibernate-core/src/main/java/org/hibernate/sql/SimpleSelect.java index e580cd3eee..10b73428a3 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/SimpleSelect.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/SimpleSelect.java @@ -18,7 +18,7 @@ import org.hibernate.LockMode; import org.hibernate.LockOptions; import org.hibernate.dialect.Dialect; import org.hibernate.engine.spi.SessionFactoryImplementor; -import org.hibernate.sql.ast.spi.JdbcParameterRenderer; +import org.hibernate.sql.ast.spi.ParameterMarkerStrategy; /** * A SQL {@code SELECT} statement with no table joins. @@ -37,20 +37,18 @@ public class SimpleSelect implements RestrictionRenderingContext { protected LockOptions lockOptions = new LockOptions( LockMode.READ ); - private final SessionFactoryImplementor factory; private final Dialect dialect; - private final JdbcParameterRenderer jdbcParameterRenderer; + private final ParameterMarkerStrategy parameterMarkerStrategy; private int parameterCount; public SimpleSelect(SessionFactoryImplementor factory) { - this.factory = factory; this.dialect = factory.getJdbcServices().getDialect(); - this.jdbcParameterRenderer = factory.getServiceRegistry().getService( JdbcParameterRenderer.class ); + this.parameterMarkerStrategy = factory.getServiceRegistry().getService( ParameterMarkerStrategy.class ); } @Override public String makeParameterMarker() { - return jdbcParameterRenderer.renderJdbcParameter( ++parameterCount, null ); + return parameterMarkerStrategy.createMarker( ++parameterCount, null ); } /** @@ -104,7 +102,7 @@ public class SimpleSelect implements RestrictionRenderingContext { /** * Appends a restriction comparing the {@code columnName} for equality with a parameter * - * @see #addRestriction(String, String, String) + * @see #addRestriction(String, ComparisonRestriction.Operator, String) */ public SimpleSelect addRestriction(String columnName) { restrictions.add( new ComparisonRestriction( columnName ) ); @@ -114,10 +112,10 @@ public class SimpleSelect implements RestrictionRenderingContext { /** * Appends a restriction based on the comparison between {@code lhs} and {@code rhs}. *

- * The {@code rhs} is checked for parameter marker and processed via {@link JdbcParameterRenderer} + * The {@code rhs} is checked for parameter marker and processed via {@link ParameterMarkerStrategy} * if needed. */ - public SimpleSelect addRestriction(String lhs, String op, String rhs) { + public SimpleSelect addRestriction(String lhs, ComparisonRestriction.Operator op, String rhs) { restrictions.add( new ComparisonRestriction( lhs, op, rhs ) ); return this; } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/Update.java b/hibernate-core/src/main/java/org/hibernate/sql/Update.java index 30a6584843..68ee353d90 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/Update.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/Update.java @@ -14,7 +14,7 @@ import java.util.Map; import org.hibernate.Internal; import org.hibernate.dialect.Dialect; import org.hibernate.engine.spi.SessionFactoryImplementor; -import org.hibernate.sql.ast.spi.JdbcParameterRenderer; +import org.hibernate.sql.ast.spi.ParameterMarkerStrategy; /** * A SQL {@code UPDATE} statement. @@ -28,17 +28,15 @@ public class Update implements RestrictionRenderingContext { protected Map assignments = new LinkedHashMap<>(); protected List restrictions = new ArrayList<>(); - private final JdbcParameterRenderer jdbcParameterRenderer; - private final boolean standardParamRendering; + private final ParameterMarkerStrategy parameterMarkerStrategy; private int parameterCount; public Update(SessionFactoryImplementor factory) { - this( factory.getServiceRegistry().getService( JdbcParameterRenderer.class ) ); + this( factory.getServiceRegistry().getService( ParameterMarkerStrategy.class ) ); } - public Update(JdbcParameterRenderer jdbcParameterRenderer) { - this.jdbcParameterRenderer = jdbcParameterRenderer; - this.standardParamRendering = JdbcParameterRenderer.isStandardRenderer( jdbcParameterRenderer ); + public Update(ParameterMarkerStrategy parameterMarkerStrategy) { + this.parameterMarkerStrategy = parameterMarkerStrategy; } public String getTableName() { @@ -91,19 +89,26 @@ public class Update implements RestrictionRenderingContext { return this; } - public Update addRestriction(String column, String op, String value) { + public Update addRestriction(String column, ComparisonRestriction.Operator op, String value) { restrictions.add( new ComparisonRestriction( column, op, value ) ); return this; } private String normalizeExpressionFragment(String rhs) { - return rhs.equals( "?" ) && !standardParamRendering - ? jdbcParameterRenderer.renderJdbcParameter( ++parameterCount, null ) + return rhs.equals( "?" ) + ? parameterMarkerStrategy.createMarker( ++parameterCount, null ) : rhs; } - public Update addNullnessRestriction(String column) { - restrictions.add( new NullnessRestriction( column ) ); + @SuppressWarnings("UnusedReturnValue") + public Update addColumnIsNullRestriction(String columnName) { + restrictions.add( new NullnessRestriction( columnName ) ); + return this; + } + + @SuppressWarnings("UnusedReturnValue") + public Update addColumnIsNotNullRestriction(String columnName) { + restrictions.add( new NullnessRestriction( columnName, false ) ); return this; } @@ -158,6 +163,6 @@ public class Update implements RestrictionRenderingContext { @Override public String makeParameterMarker() { - return jdbcParameterRenderer.renderJdbcParameter( ++parameterCount, null ); + return parameterMarkerStrategy.createMarker( ++parameterCount, null ); } } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/internal/JdbcParameterRendererStandard.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/internal/JdbcParameterRendererStandard.java deleted file mode 100644 index c8b5bc5de5..0000000000 --- a/hibernate-core/src/main/java/org/hibernate/sql/ast/internal/JdbcParameterRendererStandard.java +++ /dev/null @@ -1,25 +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.sql.ast.internal; - -import org.hibernate.sql.ast.spi.JdbcParameterRenderer; -import org.hibernate.type.descriptor.jdbc.JdbcType; - -/** - * @author Steve Ebersole - */ -public class JdbcParameterRendererStandard implements JdbcParameterRenderer { - /** - * Singleton access - */ - public static final JdbcParameterRendererStandard INSTANCE = new JdbcParameterRendererStandard(); - - @Override - public String renderJdbcParameter(int position, JdbcType jdbcType) { - return "?"; - } -} diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/internal/JdbcParameterRendererInitiator.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/internal/ParameterMarkerStrategyInitiator.java similarity index 53% rename from hibernate-core/src/main/java/org/hibernate/sql/ast/internal/JdbcParameterRendererInitiator.java rename to hibernate-core/src/main/java/org/hibernate/sql/ast/internal/ParameterMarkerStrategyInitiator.java index ee99357ba7..6006e9a868 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/ast/internal/JdbcParameterRendererInitiator.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/internal/ParameterMarkerStrategyInitiator.java @@ -14,35 +14,39 @@ import org.hibernate.dialect.Dialect; import org.hibernate.engine.jdbc.spi.JdbcServices; import org.hibernate.internal.util.config.ConfigurationHelper; import org.hibernate.service.spi.ServiceRegistryImplementor; -import org.hibernate.sql.ast.spi.JdbcParameterRenderer; +import org.hibernate.sql.ast.spi.ParameterMarkerStrategy; /** * @author Steve Ebersole */ -public class JdbcParameterRendererInitiator implements StandardServiceInitiator { +public class ParameterMarkerStrategyInitiator implements StandardServiceInitiator { /** * Singleton access */ - public static final JdbcParameterRendererInitiator INSTANCE = new JdbcParameterRendererInitiator(); + public static final ParameterMarkerStrategyInitiator INSTANCE = new ParameterMarkerStrategyInitiator(); @Override - public JdbcParameterRenderer initiateService(Map configurationValues, ServiceRegistryImplementor registry) { + public ParameterMarkerStrategy initiateService(Map configurationValues, ServiceRegistryImplementor registry) { final boolean useNativeMarkers = ConfigurationHelper.getBoolean( AvailableSettings.DIALECT_NATIVE_PARAM_MARKERS, configurationValues, - true + false ); if ( useNativeMarkers ) { final Dialect dialect = registry.getService( JdbcServices.class ).getDialect(); - return dialect.getNativeParameterRenderer(); + final ParameterMarkerStrategy nativeParameterMarkerStrategy = dialect.getNativeParameterMarkerStrategy(); + // the Dialect may return `null`, indicating falling-through to the standard strategy + if ( nativeParameterMarkerStrategy != null ) { + return nativeParameterMarkerStrategy; + } } - return JdbcParameterRendererStandard.INSTANCE; + return ParameterMarkerStrategyStandard.INSTANCE; } @Override - public Class getServiceInitiated() { - return JdbcParameterRenderer.class; + public Class getServiceInitiated() { + return ParameterMarkerStrategy.class; } } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/internal/ParameterMarkerStrategyStandard.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/internal/ParameterMarkerStrategyStandard.java new file mode 100644 index 0000000000..422f747c01 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/internal/ParameterMarkerStrategyStandard.java @@ -0,0 +1,31 @@ +/* + * 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.sql.ast.internal; + +import org.hibernate.sql.ast.spi.ParameterMarkerStrategy; +import org.hibernate.type.descriptor.jdbc.JdbcType; + +/** + * The standard ParameterMarkerStrategy based on the standard JDBC {@code ?} marker + * + * @author Steve Ebersole + */ +public class ParameterMarkerStrategyStandard implements ParameterMarkerStrategy { + /** + * Singleton access + */ + public static final ParameterMarkerStrategyStandard INSTANCE = new ParameterMarkerStrategyStandard(); + + @Override + public String createMarker(int position, JdbcType jdbcType) { + return "?"; + } + + public static boolean isStandardRenderer(ParameterMarkerStrategy check) { + return check == null || ParameterMarkerStrategyStandard.class.equals( check.getClass() ); + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/AbstractSqlAstTranslator.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/AbstractSqlAstTranslator.java index f9feae5742..0ae27d5c0c 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/AbstractSqlAstTranslator.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/AbstractSqlAstTranslator.java @@ -84,6 +84,7 @@ import org.hibernate.sql.ast.SqlAstJoinType; import org.hibernate.sql.ast.SqlAstNodeRenderingMode; import org.hibernate.sql.ast.SqlAstTranslator; import org.hibernate.sql.ast.SqlTreeCreationException; +import org.hibernate.sql.ast.internal.ParameterMarkerStrategyStandard; import org.hibernate.sql.ast.tree.MutationStatement; import org.hibernate.sql.ast.tree.SqlAstNode; import org.hibernate.sql.ast.tree.Statement; @@ -273,7 +274,7 @@ public abstract class AbstractSqlAstTranslator implemen private JdbcParameterBindings jdbcParameterBindings; private Map appliedParameterBindings = Collections.emptyMap(); private SqlAstNodeRenderingMode parameterRenderingMode = SqlAstNodeRenderingMode.DEFAULT; - private final JdbcParameterRenderer jdbcParameterRenderer; + private final ParameterMarkerStrategy parameterMarkerStrategy; private final Stack clauseStack = new StandardStack<>( Clause.class ); @@ -317,7 +318,7 @@ public abstract class AbstractSqlAstTranslator implemen this.sessionFactory = sessionFactory; this.dialect = sessionFactory.getJdbcServices().getDialect(); this.statementStack.push( statement ); - this.jdbcParameterRenderer = sessionFactory.getServiceRegistry().getService( JdbcParameterRenderer.class ); + this.parameterMarkerStrategy = sessionFactory.getServiceRegistry().getService( ParameterMarkerStrategy.class ); } private static Clause matchWithClause(Clause clause) { @@ -5073,8 +5074,8 @@ public abstract class AbstractSqlAstTranslator implemen // If we encounter a plain literal in the select clause which has no literal formatter, we must render it as parameter if ( literalFormatter == null ) { parameterBinders.add( literal ); - - final LiteralAsParameter jdbcParameter = new LiteralAsParameter<>( literal ); + final String marker = parameterMarkerStrategy.createMarker( parameterBinders.size(), literal.getJdbcMapping().getJdbcType() ); + final LiteralAsParameter jdbcParameter = new LiteralAsParameter<>( literal, marker ); if ( castParameter ) { renderCasted( jdbcParameter ); } @@ -6187,14 +6188,21 @@ public abstract class AbstractSqlAstTranslator implemen jdbcParameters.addParameter( jdbcParameter ); } - protected void renderParameterAsParameter(JdbcParameter jdbcParameter) { - renderParameterAsParameter( jdbcParameter, parameterBinders.size() + 1 ); - } - - protected void renderParameterAsParameter(JdbcParameter jdbcParameter, int position) { + protected final void renderParameterAsParameter(JdbcParameter jdbcParameter) { final JdbcType jdbcType = jdbcParameter.getExpressionType().getJdbcMappings().get( 0 ).getJdbcType(); assert jdbcType != null; - final String parameterMarker = jdbcParameterRenderer.renderJdbcParameter( position, jdbcType ); + renderParameterAsParameter( parameterBinders.size() + 1, jdbcParameter ); + } + + /** + * Renders a parameter marker for the given position + * @param jdbcParameter + * @param position + */ + protected void renderParameterAsParameter(int position, JdbcParameter jdbcParameter) { + final JdbcType jdbcType = jdbcParameter.getExpressionType().getJdbcMappings().get( 0 ).getJdbcType(); + assert jdbcType != null; + final String parameterMarker = parameterMarkerStrategy.createMarker( position, jdbcType ); jdbcType.appendWriteExpression( parameterMarker, this, dialect ); } @@ -8018,7 +8026,7 @@ public abstract class AbstractSqlAstTranslator implemen // if there are no parameters or if we are using the standard parameter renderer // - the rendering is pretty simple if ( CollectionHelper.isEmpty( columnWriteFragment.getParameters() ) - || JdbcParameterRenderer.isStandardRenderer( jdbcParameterRenderer ) ) { + || ParameterMarkerStrategyStandard.isStandardRenderer( parameterMarkerStrategy ) ) { simpleColumnWriteFragmentRendering( columnWriteFragment ); return; } @@ -8034,7 +8042,7 @@ public abstract class AbstractSqlAstTranslator implemen // to the index of the parameter marker appendSql( sqlFragment.substring( lastEnd, markerStart ) ); - // render the parameter marker and register it + // render the parameter marker and register the parameter handling visitParameterAsParameter( parameter ); lastEnd = markerStart + 1; diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/JdbcParameterRenderer.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/JdbcParameterRenderer.java deleted file mode 100644 index b99a4c0e0d..0000000000 --- a/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/JdbcParameterRenderer.java +++ /dev/null @@ -1,39 +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.sql.ast.spi; - -import org.hibernate.service.Service; -import org.hibernate.sql.ast.internal.JdbcParameterRendererStandard; -import org.hibernate.type.descriptor.jdbc.JdbcType; - -/** - * Extension point for rendering parameter markers. - *

- * Generally Hibernate will use the JDBC standard marker - {@code ?}. Many - * databases support alternative marker syntax, often numbered. - *

- * Originally developed as an extension point for use from Hibernate Reactive - * to handle the fact that some Vert.X drivers (ok, their PGSQL driver) only - * support native parameter marker syntax instead of the JDBC standard - * - * @see org.hibernate.cfg.AvailableSettings#DIALECT_NATIVE_PARAM_MARKERS - * - * @author Steve Ebersole - */ -public interface JdbcParameterRenderer extends Service { - /** - * Render the parameter for the given position - * - * @param position The 1-based position of the parameter. - * @param jdbcType The type of the parameter, if known - */ - String renderJdbcParameter(int position, JdbcType jdbcType); - - static boolean isStandardRenderer(JdbcParameterRenderer check) { - return check == null || JdbcParameterRendererStandard.class.equals( check.getClass() ); - } -} diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/ParameterMarkerStrategy.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/ParameterMarkerStrategy.java new file mode 100644 index 0000000000..a157b3d08d --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/ParameterMarkerStrategy.java @@ -0,0 +1,33 @@ +/* + * 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.sql.ast.spi; + +import org.hibernate.service.Service; +import org.hibernate.type.descriptor.jdbc.JdbcType; + +/** + * Strategy for generating parameter markers used in {@linkplain java.sql.PreparedStatement preparable} SQL strings. + *

+ * Generally Hibernate will use the JDBC standard marker - {@code ?}. Many JDBC drivers support the + * use of the "native" marker syntax of the underlying database - e.g. {@code $n}, {@code ?n}, ... + * + * @implNote Originally developed as an extension point for use from Hibernate Reactive + * for Vert.X PostgreSQL drivers which only support the native {@code $n} syntax. + * + * @see org.hibernate.cfg.AvailableSettings#DIALECT_NATIVE_PARAM_MARKERS + * + * @author Steve Ebersole + */ +public interface ParameterMarkerStrategy extends Service { + /** + * Create a parameter marker + * + * @param position The 1-based position of the parameter. + * @param jdbcType The type of the parameter, if known - may be {@code null}. + */ + String createMarker(int position, JdbcType jdbcType); +} diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/expression/LiteralAsParameter.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/expression/LiteralAsParameter.java index 99272f50a2..39385d464a 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/expression/LiteralAsParameter.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/expression/LiteralAsParameter.java @@ -19,16 +19,17 @@ import org.hibernate.sql.ast.spi.SqlAppender; * @author Christian Beikov */ public class LiteralAsParameter implements SelfRenderingExpression { - private final Literal literal; + private final String parameterMarker; - public LiteralAsParameter(Literal literal) { + public LiteralAsParameter(Literal literal, String parameterMarker) { this.literal = literal; + this.parameterMarker = parameterMarker; } @Override public void renderToSql(SqlAppender sqlAppender, SqlAstTranslator walker, SessionFactoryImplementor sessionFactory) { - sqlAppender.appendSql( "?" ); + sqlAppender.append( parameterMarker ); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/sql/exec/internal/JdbcParameterBindingsImpl.java b/hibernate-core/src/main/java/org/hibernate/sql/exec/internal/JdbcParameterBindingsImpl.java index f082ea87a6..1b9e7a8840 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/exec/internal/JdbcParameterBindingsImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/exec/internal/JdbcParameterBindingsImpl.java @@ -9,12 +9,25 @@ package org.hibernate.sql.exec.internal; import java.util.Collection; import java.util.Collections; import java.util.IdentityHashMap; +import java.util.List; import java.util.Map; import java.util.function.BiConsumer; +import org.hibernate.dialect.Dialect; +import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.metamodel.mapping.BasicValuedMapping; +import org.hibernate.metamodel.mapping.JdbcMapping; +import org.hibernate.query.BindableType; +import org.hibernate.query.spi.QueryParameterBinding; +import org.hibernate.query.spi.QueryParameterBindings; +import org.hibernate.query.spi.QueryParameterImplementor; +import org.hibernate.query.sql.internal.NativeQueryImpl; +import org.hibernate.query.sql.spi.ParameterOccurrence; import org.hibernate.sql.ast.tree.expression.JdbcParameter; +import org.hibernate.sql.exec.spi.JdbcParameterBinder; import org.hibernate.sql.exec.spi.JdbcParameterBinding; import org.hibernate.sql.exec.spi.JdbcParameterBindings; +import org.hibernate.type.BasicTypeReference; /** * Standard implementation of JdbcParameterBindings @@ -30,6 +43,83 @@ public class JdbcParameterBindingsImpl implements JdbcParameterBindings { } } + public JdbcParameterBindingsImpl( + QueryParameterBindings queryParameterBindings, + List parameterOccurrences, + List jdbcParameterBinders, + SessionFactoryImplementor factory) { + if ( !parameterOccurrences.isEmpty() ) { + bindingMap = new IdentityHashMap<>( parameterOccurrences.size() ); + + final Dialect dialect = factory.getJdbcServices().getDialect(); + final boolean paddingEnabled = factory.getSessionFactoryOptions().inClauseParameterPaddingEnabled(); + final int inExprLimit = dialect.getInExpressionCountLimit(); + + for ( ParameterOccurrence occurrence : parameterOccurrences ) { + final QueryParameterImplementor param = occurrence.getParameter(); + final QueryParameterBinding binding = queryParameterBindings.getBinding( param ); + + final JdbcMapping jdbcMapping; + + final BindableType type = determineParamType( param, binding ); + if ( type == null ) { + jdbcMapping = factory.getTypeConfiguration().getBasicTypeForJavaType( Object.class ); + } + else if ( type instanceof BasicTypeReference ) { + jdbcMapping = factory.getTypeConfiguration() + .getBasicTypeRegistry() + .resolve( ( (BasicTypeReference) type ) ); + } + else if ( type instanceof BasicValuedMapping ) { + jdbcMapping = ( (BasicValuedMapping) type ).getJdbcMapping(); + } + else { + throw new IllegalArgumentException( "Could not resolve NativeQuery parameter type : `" + param + "`"); + } + + if ( binding.isMultiValued() ) { + final Collection bindValues = binding.getBindValues(); + final int bindValueCount = bindValues.size(); + Object lastBindValue = null; + for ( Object bindValue : bindValues ) { + final JdbcParameterImpl jdbcParameter = new JdbcParameterImpl( jdbcMapping ); + jdbcParameterBinders.add( jdbcParameter ); + addBinding( jdbcParameter, new JdbcParameterBindingImpl( jdbcMapping, bindValue ) ); + lastBindValue = bindValue; + } + final int bindValueMaxCount = NativeQueryImpl.determineBindValueMaxCount( + paddingEnabled, + inExprLimit, + bindValueCount + ); + if ( bindValueMaxCount != bindValueCount ) { + for ( int i = bindValueCount; i < bindValueMaxCount; i++ ) { + final JdbcParameterImpl jdbcParameter = new JdbcParameterImpl( jdbcMapping ); + jdbcParameterBinders.add( jdbcParameter ); + addBinding( jdbcParameter, new JdbcParameterBindingImpl( jdbcMapping, lastBindValue ) ); + } + } + } + else { + final JdbcParameterImpl jdbcParameter = new JdbcParameterImpl( jdbcMapping ); + jdbcParameterBinders.add( jdbcParameter ); + addBinding( + jdbcParameter, + new JdbcParameterBindingImpl( jdbcMapping, binding.getBindValue() ) + ); + } + } + } + } + + private BindableType determineParamType(QueryParameterImplementor param, QueryParameterBinding binding) { + BindableType type = binding.getBindType(); + if ( type == null ) { + type = param.getHibernateType(); + } + return type; + } + @Override public void addBinding(JdbcParameter parameter, JdbcParameterBinding binding) { if ( bindingMap == null ) { @@ -47,7 +137,6 @@ public class JdbcParameterBindingsImpl implements JdbcParameterBindings { @Override public JdbcParameterBinding getBinding(JdbcParameter parameter) { if ( bindingMap == null ) { - // no bindings return null; } return bindingMap.get( parameter ); @@ -55,6 +144,9 @@ public class JdbcParameterBindingsImpl implements JdbcParameterBindings { @Override public void visitBindings(BiConsumer action) { + if ( bindingMap == null ) { + return; + } for ( Map.Entry entry : bindingMap.entrySet() ) { action.accept( entry.getKey(), entry.getValue() ); } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/exec/spi/JdbcParameterBindings.java b/hibernate-core/src/main/java/org/hibernate/sql/exec/spi/JdbcParameterBindings.java index 852f7ce537..37562a5ded 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/exec/spi/JdbcParameterBindings.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/exec/spi/JdbcParameterBindings.java @@ -11,34 +11,24 @@ import java.util.Collections; import java.util.List; import java.util.function.BiConsumer; -import org.hibernate.dialect.Dialect; -import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor; -import org.hibernate.metamodel.mapping.BasicValuedMapping; import org.hibernate.metamodel.mapping.Bindable; import org.hibernate.metamodel.mapping.JdbcMapping; -import org.hibernate.query.BindableType; import org.hibernate.query.internal.BindingTypeHelper; -import org.hibernate.query.spi.QueryParameterBinding; -import org.hibernate.query.spi.QueryParameterBindings; -import org.hibernate.query.spi.QueryParameterImplementor; -import org.hibernate.query.sql.internal.NativeQueryImpl; -import org.hibernate.query.sql.spi.ParameterOccurrence; import org.hibernate.sql.ast.tree.expression.JdbcParameter; import org.hibernate.sql.exec.internal.JdbcParameterBindingImpl; -import org.hibernate.sql.exec.internal.JdbcParameterImpl; -import org.hibernate.type.BasicTypeReference; +import org.hibernate.type.spi.TypeConfiguration; /** - * Access to all of the externalized JDBC parameter bindings + * Access to all the externalized JDBC parameter bindings * * @apiNote "Externalized" because some JDBC parameter values are - * intrinsically part of the parameter itself and we do not need to + * intrinsically part of the parameter itself, and we do not need to * locate a JdbcParameterBinding. E.g., consider a * {@link org.hibernate.sql.ast.tree.expression.LiteralAsParameter} - * which actually encapsulates the actually literal value inside - * itself - to create the binder and actually perform the binding - * is only dependent on the LiteralParameter + * which encapsulates the literal value inside itself - to create the + * binder and actually perform the binding is only dependent on the + * LiteralParameter * * @author Steve Ebersole */ @@ -90,94 +80,27 @@ public interface JdbcParameterBindings { offset, jdbcParameters, session.getFactory().getTypeConfiguration(), - (selectionIndex, params, typeConfiguration, jdbcValue, type) -> { - addBinding( - params.get( selectionIndex ), - new JdbcParameterBindingImpl( - BindingTypeHelper.INSTANCE.resolveBindType( - jdbcValue, - type, - typeConfiguration - ), - jdbcValue - ) - ); - } - , + this::createAndAddBinding, session ); } - default void registerNativeQueryParameters( - QueryParameterBindings queryParameterBindings, - List parameterOccurrences, - List jdbcParameterBinders, - SessionFactoryImplementor factory) { - final Dialect dialect = factory.getJdbcServices().getDialect(); - final boolean paddingEnabled = factory.getSessionFactoryOptions().inClauseParameterPaddingEnabled(); - final int inExprLimit = dialect.getInExpressionCountLimit(); - - for ( ParameterOccurrence occurrence : parameterOccurrences ) { - final QueryParameterImplementor param = occurrence.getParameter(); - final QueryParameterBinding binding = queryParameterBindings.getBinding( param ); - - final JdbcMapping jdbcMapping; - - final BindableType type = determineParamType( param, binding ); - if ( type == null ) { - jdbcMapping = factory.getTypeConfiguration().getBasicTypeForJavaType( Object.class ); - } - else if ( type instanceof BasicTypeReference ) { - jdbcMapping = factory.getTypeConfiguration() - .getBasicTypeRegistry() - .resolve( ( (BasicTypeReference) type ) ); - } - else if ( type instanceof BasicValuedMapping ) { - jdbcMapping = ( (BasicValuedMapping) type ).getJdbcMapping(); - } - else { - throw new IllegalArgumentException( "Could not resolve NativeQuery parameter type : `" + param + "`"); - } - - if ( binding.isMultiValued() ) { - final Collection bindValues = binding.getBindValues(); - final int bindValueCount = bindValues.size(); - Object lastBindValue = null; - for ( Object bindValue : bindValues ) { - final JdbcParameterImpl jdbcParameter = new JdbcParameterImpl( jdbcMapping ); - jdbcParameterBinders.add( jdbcParameter ); - addBinding( jdbcParameter, new JdbcParameterBindingImpl( jdbcMapping, bindValue ) ); - lastBindValue = bindValue; - } - final int bindValueMaxCount = NativeQueryImpl.determineBindValueMaxCount( - paddingEnabled, - inExprLimit, - bindValueCount - ); - if ( bindValueMaxCount != bindValueCount ) { - for ( int i = bindValueCount; i < bindValueMaxCount; i++ ) { - final JdbcParameterImpl jdbcParameter = new JdbcParameterImpl( jdbcMapping ); - jdbcParameterBinders.add( jdbcParameter ); - addBinding( jdbcParameter, new JdbcParameterBindingImpl( jdbcMapping, lastBindValue ) ); - } - } - } - else { - final JdbcParameterImpl jdbcParameter = new JdbcParameterImpl( jdbcMapping ); - jdbcParameterBinders.add( jdbcParameter ); - addBinding( - jdbcParameter, - new JdbcParameterBindingImpl( jdbcMapping, binding.getBindValue() ) - ); - } - } - } - - private BindableType determineParamType(QueryParameterImplementor param, QueryParameterBinding binding) { - BindableType type = binding.getBindType(); - if ( type == null ) { - type = param.getHibernateType(); - } - return type; + private void createAndAddBinding( + int selectionIndex, + List params, + TypeConfiguration typeConfiguration, + Object jdbcValue, + JdbcMapping type) { + addBinding( + params.get( selectionIndex ), + new JdbcParameterBindingImpl( + BindingTypeHelper.INSTANCE.resolveBindType( + jdbcValue, + type, + typeConfiguration + ), + jdbcValue + ) + ); } } diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/collection/list/PersistentListTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/collection/list/PersistentListTest.java index cf0da63594..384d8402c9 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/collection/list/PersistentListTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/collection/list/PersistentListTest.java @@ -17,6 +17,7 @@ import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionImplementor; import org.hibernate.persister.collection.CollectionPersister; import org.hibernate.persister.collection.QueryableCollection; +import org.hibernate.sql.ComparisonRestriction; import org.hibernate.sql.SimpleSelect; import org.hibernate.testing.TestForIssue; @@ -76,7 +77,7 @@ public class PersistentListTest { .setTableName( queryableCollection.getTableName() ) .addColumn( "NAME" ) .addColumn( "LIST_INDEX" ) - .addRestriction( "NAME", "<>", "?" ); + .addRestriction( "NAME", ComparisonRestriction.Operator.NE, "?" ); PreparedStatement preparedStatement = ( (SessionImplementor) session2 ).getJdbcCoordinator() .getStatementPreparer() .prepareStatement( select.toStatementString() ); diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/sql/ast/JdbcParameterRendererTests.java b/hibernate-core/src/test/java/org/hibernate/orm/test/sql/ast/JdbcParameterRendererTests.java deleted file mode 100644 index 2ebecccacb..0000000000 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/sql/ast/JdbcParameterRendererTests.java +++ /dev/null @@ -1,163 +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.orm.test.sql.ast; - -import org.hibernate.annotations.Filter; -import org.hibernate.annotations.FilterDef; -import org.hibernate.annotations.ParamDef; -import org.hibernate.dialect.Dialect; -import org.hibernate.dialect.H2Dialect; -import org.hibernate.internal.util.StringHelper; -import org.hibernate.sql.ast.spi.JdbcParameterRenderer; -import org.hibernate.sql.ast.spi.SqlAppender; -import org.hibernate.type.descriptor.jdbc.JdbcType; - -import org.hibernate.testing.jdbc.SQLStatementInspector; -import org.hibernate.testing.orm.domain.gambit.EntityOfBasics; -import org.hibernate.testing.orm.junit.DomainModel; -import org.hibernate.testing.orm.junit.RequiresDialect; -import org.hibernate.testing.orm.junit.ServiceRegistry; -import org.hibernate.testing.orm.junit.SessionFactory; -import org.hibernate.testing.orm.junit.SessionFactoryScope; -import org.junit.jupiter.api.Test; - -import jakarta.persistence.Basic; -import jakarta.persistence.Entity; -import jakarta.persistence.Id; -import jakarta.persistence.Table; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * @implNote Restricted to H2 as there is nothing intrinsically Dialect specific here, - * though each database has specific syntax for labelled parameters - * - * @author Steve Ebersole - */ -@ServiceRegistry( services = @ServiceRegistry.Service( - role = JdbcParameterRenderer.class, - impl = JdbcParameterRendererTests.JdbcParameterRendererImpl.class -) ) -@DomainModel( annotatedClasses = { EntityOfBasics.class, JdbcParameterRendererTests.EntityWithFilters.class } ) -@SessionFactory( useCollectingStatementInspector = true ) -@RequiresDialect( H2Dialect.class ) -public class JdbcParameterRendererTests { - @Test - public void basicTest(SessionFactoryScope scope) { - final String queryString = "select e from EntityOfBasics e where e.id = :id"; - - final SQLStatementInspector statementInspector = scope.getCollectingStatementInspector(); - statementInspector.clear(); - - scope.inTransaction( (session) -> { - session.createSelectionQuery( queryString, EntityOfBasics.class ).setParameter( "id", 1 ).list(); - } ); - - assertThat( statementInspector.getSqlQueries() ).hasSize( 1 ); - final String sql = statementInspector.getSqlQueries().get( 0 ); - assertThat( sql ).contains( "?1" ); - } - - @Test - public void testFilters(SessionFactoryScope scope) { - final SQLStatementInspector statementInspector = scope.getCollectingStatementInspector(); - statementInspector.clear(); - - scope.inTransaction( (session) -> { - session.enableFilter( "region" ).setParameter( "region", "NA" ); - session.createSelectionQuery( "from EntityWithFilters", EntityWithFilters.class ).list(); - assertThat( statementInspector.getSqlQueries() ).hasSize( 1 ); - final String sql = statementInspector.getSqlQueries().get( 0 ); - assertThat( sql ).contains( "?1" ); - } ); - - statementInspector.clear(); - scope.inTransaction( (session) -> { - final EntityWithFilters it = new EntityWithFilters( 1, "It", "EMEA" ); - session.persist( it ); - } ); - assertThat( statementInspector.getSqlQueries() ).hasSize( 1 ); - assertThat( StringHelper.count( statementInspector.getSqlQueries().get( 0 ), "?" ) ).isEqualTo( 3 ); - assertThat( statementInspector.getSqlQueries().get( 0 ) ).contains( "?1" ); - assertThat( statementInspector.getSqlQueries().get( 0 ) ).contains( "?2" ); - assertThat( statementInspector.getSqlQueries().get( 0 ) ).contains( "?3" ); - - scope.inTransaction( (session) -> { - final EntityWithFilters it = session.find( EntityWithFilters.class, 1 ); - statementInspector.clear(); - it.setName( "It 2" ); - } ); - assertThat( statementInspector.getSqlQueries() ).hasSize( 1 ); - assertThat( StringHelper.count( statementInspector.getSqlQueries().get( 0 ), "?" ) ).isEqualTo( 3 ); - assertThat( statementInspector.getSqlQueries().get( 0 ) ).contains( "?1" ); - assertThat( statementInspector.getSqlQueries().get( 0 ) ).contains( "?2" ); - assertThat( statementInspector.getSqlQueries().get( 0 ) ).contains( "?3" ); - - scope.inTransaction( (session) -> { - final EntityWithFilters it = session.find( EntityWithFilters.class, 1 ); - statementInspector.clear(); - session.remove( it ); - } ); - assertThat( statementInspector.getSqlQueries() ).hasSize( 1 ); - assertThat( StringHelper.count( statementInspector.getSqlQueries().get( 0 ), "?" ) ).isEqualTo( 1 ); - assertThat( statementInspector.getSqlQueries().get( 0 ) ).contains( "?1" ); - } - - public static class JdbcParameterRendererImpl implements JdbcParameterRenderer { - @Override - public String renderJdbcParameter(int position, JdbcType jdbcType) { - return "?" + position; - } - } - - @Entity( name = "EntityWithFilters" ) - @Table( name = "filtered_entity" ) - @FilterDef( - name = "region", - defaultCondition = "region = :region", - parameters = @ParamDef(name = "region", type = String.class) - ) - @Filter( name = "region" ) - public static class EntityWithFilters { - @Id - private Integer id; - @Basic - private String name; - @Basic - private String region; - - protected EntityWithFilters() { - // for use by Hibernate - } - - public EntityWithFilters(Integer id, String name, String region) { - this.id = id; - this.name = name; - this.region = region; - } - - public Integer getId() { - return id; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public String getRegion() { - return region; - } - - public void setRegion(String region) { - this.region = region; - } - } -} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/sql/ast/ParameterMarkerStrategyTests.java b/hibernate-core/src/test/java/org/hibernate/orm/test/sql/ast/ParameterMarkerStrategyTests.java new file mode 100644 index 0000000000..024bb0b079 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/sql/ast/ParameterMarkerStrategyTests.java @@ -0,0 +1,276 @@ +/* + * 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.sql.ast; + +import java.util.List; + +import org.hibernate.LockMode; +import org.hibernate.annotations.Filter; +import org.hibernate.annotations.FilterDef; +import org.hibernate.annotations.ParamDef; +import org.hibernate.dialect.H2Dialect; +import org.hibernate.internal.util.StringHelper; +import org.hibernate.sql.ast.spi.ParameterMarkerStrategy; +import org.hibernate.type.descriptor.jdbc.JdbcType; + +import org.hibernate.testing.jdbc.SQLStatementInspector; +import org.hibernate.testing.orm.domain.gambit.EntityOfBasics; +import org.hibernate.testing.orm.junit.DomainModel; +import org.hibernate.testing.orm.junit.FailureExpected; +import org.hibernate.testing.orm.junit.Jira; +import org.hibernate.testing.orm.junit.RequiresDialect; +import org.hibernate.testing.orm.junit.ServiceRegistry; +import org.hibernate.testing.orm.junit.SessionFactory; +import org.hibernate.testing.orm.junit.SessionFactoryScope; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Test; + +import jakarta.persistence.Basic; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.Table; +import jakarta.persistence.Version; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.hibernate.internal.util.StringHelper.*; + +/** + * @implNote Restricted to H2 as there is nothing intrinsically Dialect specific here, + * though each database has specific syntax for labelled parameters + * + * @author Steve Ebersole + */ +@ServiceRegistry( services = @ServiceRegistry.Service( + role = ParameterMarkerStrategy.class, + impl = ParameterMarkerStrategyTests.ParameterMarkerStrategyImpl.class +) ) +@DomainModel( annotatedClasses = { + EntityOfBasics.class, + ParameterMarkerStrategyTests.EntityWithFilters.class, + ParameterMarkerStrategyTests.EntityWithVersion.class +} ) +@SessionFactory( useCollectingStatementInspector = true ) +@RequiresDialect( H2Dialect.class ) +public class ParameterMarkerStrategyTests { + @Test + @Jira( "https://hibernate.atlassian.net/browse/HHH-16229" ) + public void testQueryParams(SessionFactoryScope scope) { + final String queryString = "select e from EntityOfBasics e where e.id = :id"; + + final SQLStatementInspector statementInspector = scope.getCollectingStatementInspector(); + statementInspector.clear(); + + scope.inTransaction( (session) -> { + session.createSelectionQuery( queryString, EntityOfBasics.class ).setParameter( "id", 1 ).list(); + } ); + + assertThat( statementInspector.getSqlQueries() ).hasSize( 1 ); + final String sql = statementInspector.getSqlQueries().get( 0 ); + assertThat( sql ).contains( "?1" ); + } + + @Test + @Jira( "https://hibernate.atlassian.net/browse/HHH-16260" ) + public void testFilters(SessionFactoryScope scope) { + final SQLStatementInspector statementInspector = scope.getCollectingStatementInspector(); + statementInspector.clear(); + + scope.inTransaction( (session) -> { + session.enableFilter( "region" ).setParameter( "region", "NA" ); + session.createSelectionQuery( "from EntityWithFilters", EntityWithFilters.class ).list(); + assertThat( statementInspector.getSqlQueries() ).hasSize( 1 ); + final String sql = statementInspector.getSqlQueries().get( 0 ); + assertThat( sql ).contains( "?1" ); + } ); + } + + @Test + @Jira( "https://hibernate.atlassian.net/browse/HHH-16256" ) + public void testMutations(SessionFactoryScope scope) { + final SQLStatementInspector statementInspector = scope.getCollectingStatementInspector(); + statementInspector.clear(); + + scope.inTransaction( (session) -> { + final EntityWithFilters it = new EntityWithFilters( 1, "It", "EMEA" ); + session.persist( it ); + session.flush(); + assertThat( statementInspector.getSqlQueries() ).hasSize( 1 ); + assertThat( count( statementInspector.getSqlQueries().get( 0 ), "?" ) ).isEqualTo( 3 ); + assertThat( statementInspector.getSqlQueries().get( 0 ) ).contains( "?1" ); + assertThat( statementInspector.getSqlQueries().get( 0 ) ).contains( "?2" ); + assertThat( statementInspector.getSqlQueries().get( 0 ) ).contains( "?3" ); + } ); + + scope.inTransaction( (session) -> { + final EntityWithFilters it = session.find( EntityWithFilters.class, 1 ); + statementInspector.clear(); + it.setName( "It 2" ); + session.flush(); + assertThat( statementInspector.getSqlQueries() ).hasSize( 1 ); + assertThat( count( statementInspector.getSqlQueries().get( 0 ), "?" ) ).isEqualTo( 3 ); + assertThat( statementInspector.getSqlQueries().get( 0 ) ).contains( "?1" ); + assertThat( statementInspector.getSqlQueries().get( 0 ) ).contains( "?2" ); + assertThat( statementInspector.getSqlQueries().get( 0 ) ).contains( "?3" ); + } ); + + scope.inTransaction( (session) -> { + final EntityWithFilters it = session.find( EntityWithFilters.class, 1 ); + statementInspector.clear(); + session.remove( it ); + session.flush(); + assertThat( statementInspector.getSqlQueries() ).hasSize( 1 ); + assertThat( count( statementInspector.getSqlQueries().get( 0 ), "?" ) ).isEqualTo( 1 ); + assertThat( statementInspector.getSqlQueries().get( 0 ) ).contains( "?1" ); + } ); + } + + @Test + @Jira( "https://hibernate.atlassian.net/browse/HHH-16229" ) + public void testLocking(SessionFactoryScope scope) { + final SQLStatementInspector statementInspector = scope.getCollectingStatementInspector(); + + final EntityWithVersion created = scope.fromTransaction( (session) -> { + final EntityWithVersion entity = new EntityWithVersion( 1, "Entity Prime" ); + session.persist( entity ); + return entity; + } ); + + statementInspector.clear(); + scope.inTransaction( (session) -> { + session.lock( created, LockMode.PESSIMISTIC_FORCE_INCREMENT ); + assertThat( statementInspector.getSqlQueries() ).hasSize( 1 ); + assertThat( statementInspector.getSqlQueries().get( 0 ) ).contains( "?1" ); + assertThat( statementInspector.getSqlQueries().get( 0 ) ).contains( "?2" ); + assertThat( statementInspector.getSqlQueries().get( 0 ) ).contains( "?3" ); + assertThat( statementInspector.getSqlQueries().get( 0 ) ).matches( (sql) -> count( sql, "?" ) == 3 ); + } ); + } + + @Test + @FailureExpected + @Jira( "https://hibernate.atlassian.net/browse/HHH-16283" ) + public void testNativeQuery(SessionFactoryScope scope) { + final SQLStatementInspector statementInspector = scope.getCollectingStatementInspector(); + + statementInspector.clear(); + scope.inTransaction( (session) -> { + session.createNativeQuery( "select count(1) from filtered_entity e where e.region = :region" ) + .setParameter( "region", "ABC" ) + .uniqueResult(); + + assertThat( statementInspector.getSqlQueries() ).hasSize( 1 ); + assertThat( count( statementInspector.getSqlQueries().get( 0 ), "?" ) ).isEqualTo( 1 ); + assertThat( statementInspector.getSqlQueries().get( 0 ) ).contains( "?1" ); + } ); + + statementInspector.clear(); + scope.inTransaction( (session) -> { + session.createNativeQuery( "select count(1) from filtered_entity e where e.region in (:region)" ) + .setParameterList( "region", List.of( "ABC", "DEF" ) ) + .uniqueResult(); + + assertThat( statementInspector.getSqlQueries() ).hasSize( 1 ); + assertThat( count( statementInspector.getSqlQueries().get( 0 ), "?" ) ).isEqualTo( 1 ); + assertThat( statementInspector.getSqlQueries().get( 0 ) ).contains( "?1" ); + } ); + } + + @AfterEach + public void cleanUpTestData(SessionFactoryScope scope) { + scope.inTransaction( (session) -> { + session.createMutationQuery( "delete EntityOfBasics" ).executeUpdate(); + session.createMutationQuery( "delete EntityWithFilters" ).executeUpdate(); + session.createMutationQuery( "delete EntityWithVersion" ).executeUpdate(); + } ); + } + + public static class ParameterMarkerStrategyImpl implements ParameterMarkerStrategy { + @Override + public String createMarker(int position, JdbcType jdbcType) { + return "?" + position; + } + } + + @Entity( name = "EntityWithFilters" ) + @Table( name = "filtered_entity" ) + @FilterDef( + name = "region", + defaultCondition = "region = :region", + parameters = @ParamDef(name = "region", type = String.class) + ) + @Filter( name = "region" ) + public static class EntityWithFilters { + @Id + private Integer id; + @Basic + private String name; + @Basic + private String region; + + protected EntityWithFilters() { + // for use by Hibernate + } + + public EntityWithFilters(Integer id, String name, String region) { + this.id = id; + this.name = name; + this.region = region; + } + + public Integer getId() { + return id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getRegion() { + return region; + } + + public void setRegion(String region) { + this.region = region; + } + } + + @Entity( name = "EntityWithVersion" ) + @Table( name = "versioned_entity" ) + public static class EntityWithVersion { + @Id + private Integer id; + @Basic + private String name; + @Version + private int version; + + private EntityWithVersion() { + // for use by Hibernate + } + + public EntityWithVersion(Integer id, String name) { + this.id = id; + this.name = name; + } + + public Integer getId() { + return id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + } +} diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/strategy/internal/ValidityAuditStrategy.java b/hibernate-envers/src/main/java/org/hibernate/envers/strategy/internal/ValidityAuditStrategy.java index 2de60cf7b2..ab289d8f83 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/strategy/internal/ValidityAuditStrategy.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/strategy/internal/ValidityAuditStrategy.java @@ -45,6 +45,7 @@ import org.hibernate.persister.entity.JoinedSubclassEntityPersister; import org.hibernate.persister.entity.Queryable; import org.hibernate.persister.entity.UnionSubclassEntityPersister; import org.hibernate.property.access.spi.Getter; +import org.hibernate.sql.ComparisonRestriction; import org.hibernate.sql.Update; import org.hibernate.type.BasicType; import org.hibernate.type.CollectionType; @@ -580,11 +581,11 @@ public class ValidityAuditStrategy implements AuditStrategy { // Apply "AND REV <> ?" // todo (PropertyMapping) : need to be able to handle paths final String path = configuration.getRevisionNumberPath(); - context.addRestriction( rootAuditEntity.toColumns( path )[ 0 ], "<>", "?" ); + context.addRestriction( rootAuditEntity.toColumns( path )[ 0 ], ComparisonRestriction.Operator.NE, "?" ); context.bind( revisionNumber, rootAuditEntity.getPropertyType( path ) ); // Apply "AND REVEND is null" - context.addNullnessRestriction( revEndColumnName ); + context.addColumnIsNullRestriction( revEndColumnName ); return context; } @@ -635,11 +636,11 @@ public class ValidityAuditStrategy implements AuditStrategy { // Apply "AND REV <> ?" // todo (PropertyMapping) : need to be able to handle paths - context.addRestriction( configuration.getRevisionFieldName(), "<>", "?" ); + context.addRestriction( configuration.getRevisionFieldName(), ComparisonRestriction.Operator.NE, "?" ); context.bind( revisionNumber, auditEntity.getPropertyType( configuration.getRevisionNumberPath() ) ); // Apply "AND REVEND_TSTMP is null" - context.addNullnessRestriction( revEndTimestampColumnName ); + context.addColumnIsNullRestriction( revEndTimestampColumnName ); return context; }