From 763a70f6330310cb5f2f13e430bc8e147578a88d Mon Sep 17 00:00:00 2001 From: Jan Schatteman Date: Wed, 18 Oct 2023 21:06:54 +0200 Subject: [PATCH] HHH-17314 - Add a configuration option to ignore jdbc parameters in native queries Signed-off-by: Jan Schatteman --- .../SessionFactoryOptionsBuilder.java | 12 ++ ...stractDelegatingSessionFactoryOptions.java | 4 + .../boot/spi/SessionFactoryOptions.java | 7 + .../java/org/hibernate/cfg/QuerySettings.java | 11 ++ .../NativeQueryInterpreterStandardImpl.java | 10 +- .../spi/NativeQueryInterpreterInitiator.java | 2 +- .../engine/spi/SessionDelegatorBaseImpl.java | 10 ++ .../spi/SharedSessionContractImplementor.java | 4 + .../spi/SharedSessionDelegatorBaseImpl.java | 10 ++ .../AbstractSharedSessionContract.java | 13 ++ .../query/sql/internal/NativeQueryImpl.java | 130 +++++++++--------- .../query/sql/internal/ParameterParser.java | 11 +- .../query/sql/spi/ParameterRecognizer.java | 2 +- .../query/sql/NativeQueryParameterTests.java | 21 +++ .../test/query/sql/ParameterParserTest.java | 43 ++++++ .../validation/MockSessionFactory.java | 2 +- 16 files changed, 220 insertions(+), 72 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/boot/internal/SessionFactoryOptionsBuilder.java b/hibernate-core/src/main/java/org/hibernate/boot/internal/SessionFactoryOptionsBuilder.java index 155c83e12a..e414db7d8f 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/internal/SessionFactoryOptionsBuilder.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/internal/SessionFactoryOptionsBuilder.java @@ -248,6 +248,7 @@ public class SessionFactoryOptionsBuilder implements SessionFactoryOptions { private TimeZone jdbcTimeZone; private final ValueHandlingMode criteriaValueHandlingMode; private final boolean criteriaCopyTreeEnabled; + private final boolean nativeJdbcParametersIgnored; private final ImmutableEntityUpdateQueryHandlingMode immutableEntityUpdateQueryHandlingMode; // These two settings cannot be modified from the builder, // in order to maintain consistency. @@ -551,6 +552,12 @@ else if ( jdbcTimeZoneValue != null ) { jpaBootstrap ); + this.nativeJdbcParametersIgnored = getBoolean( + AvailableSettings.NATIVE_IGNORE_JDBC_PARAMETERS, + configurationSettings, + false + ); + // added the boolean parameter in case we want to define some form of "all" as discussed this.jpaCompliance = context.getJpaCompliance(); @@ -1145,6 +1152,11 @@ public boolean isCriteriaCopyTreeEnabled() { return criteriaCopyTreeEnabled; } + @Override + public boolean getNativeJdbcParametersIgnored() { + return nativeJdbcParametersIgnored; + } + @Override public ImmutableEntityUpdateQueryHandlingMode getImmutableEntityUpdateQueryHandlingMode() { return immutableEntityUpdateQueryHandlingMode; diff --git a/hibernate-core/src/main/java/org/hibernate/boot/spi/AbstractDelegatingSessionFactoryOptions.java b/hibernate-core/src/main/java/org/hibernate/boot/spi/AbstractDelegatingSessionFactoryOptions.java index eb717bccfa..5b33b5ac05 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/spi/AbstractDelegatingSessionFactoryOptions.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/spi/AbstractDelegatingSessionFactoryOptions.java @@ -382,6 +382,10 @@ public boolean isCriteriaCopyTreeEnabled() { return delegate.isCriteriaCopyTreeEnabled(); } + public boolean getNativeJdbcParametersIgnored() { + return delegate.getNativeJdbcParametersIgnored(); + } + @Override public JpaCompliance getJpaCompliance() { return delegate.getJpaCompliance(); diff --git a/hibernate-core/src/main/java/org/hibernate/boot/spi/SessionFactoryOptions.java b/hibernate-core/src/main/java/org/hibernate/boot/spi/SessionFactoryOptions.java index f230d9d907..e8ffc846bb 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/spi/SessionFactoryOptions.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/spi/SessionFactoryOptions.java @@ -231,6 +231,13 @@ default boolean isCriteriaCopyTreeEnabled() { return false; } + /** + * @see org.hibernate.cfg.AvailableSettings#NATIVE_IGNORE_JDBC_PARAMETERS + */ + default boolean getNativeJdbcParametersIgnored() { + return false; + } + JpaCompliance getJpaCompliance(); boolean isFailOnPaginationOverCollectionFetchEnabled(); diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/QuerySettings.java b/hibernate-core/src/main/java/org/hibernate/cfg/QuerySettings.java index 3151f7553e..65083ca4ea 100644 --- a/hibernate-core/src/main/java/org/hibernate/cfg/QuerySettings.java +++ b/hibernate-core/src/main/java/org/hibernate/cfg/QuerySettings.java @@ -6,6 +6,7 @@ */ package org.hibernate.cfg; +import org.hibernate.boot.spi.SessionFactoryOptions; import org.hibernate.query.NullPrecedence; import org.hibernate.query.spi.QueryPlan; @@ -124,6 +125,16 @@ public interface QuerySettings { */ String CRITERIA_COPY_TREE = "hibernate.criteria.copy_tree"; + /** + * When set to true, indicates that ordinal parameters (represented by the '?' placeholder) in native queries will be ignored. + *

+ * By default, this is set to false, i.e. native queries will be checked for ordinal placeholders. + *

+ * + * @see SessionFactoryOptions#getIgnoreNativeJdbcParameters() + */ + String NATIVE_IGNORE_JDBC_PARAMETERS = "hibernate.query.native.ignore_jdbc_parameters"; + /** * When {@linkplain org.hibernate.query.Query#setMaxResults(int) pagination} is used * in combination with a {@code fetch join} applied to a collection or many-valued diff --git a/hibernate-core/src/main/java/org/hibernate/engine/query/internal/NativeQueryInterpreterStandardImpl.java b/hibernate-core/src/main/java/org/hibernate/engine/query/internal/NativeQueryInterpreterStandardImpl.java index fc3cc9d6d5..f67e703136 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/query/internal/NativeQueryInterpreterStandardImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/query/internal/NativeQueryInterpreterStandardImpl.java @@ -21,11 +21,17 @@ public class NativeQueryInterpreterStandardImpl implements NativeQueryInterprete /** * Singleton access */ - public static final NativeQueryInterpreterStandardImpl NATIVE_QUERY_INTERPRETER = new NativeQueryInterpreterStandardImpl(); + public static final NativeQueryInterpreterStandardImpl NATIVE_QUERY_INTERPRETER = new NativeQueryInterpreterStandardImpl( false ); + + private boolean nativeJdbcParametersIgnored; + + public NativeQueryInterpreterStandardImpl(boolean nativeJdbcParametersIgnored) { + this.nativeJdbcParametersIgnored = nativeJdbcParametersIgnored; + } @Override public void recognizeParameters(String nativeQuery, ParameterRecognizer recognizer) { - ParameterParser.parse( nativeQuery, recognizer ); + ParameterParser.parse( nativeQuery, recognizer, nativeJdbcParametersIgnored ); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/engine/query/spi/NativeQueryInterpreterInitiator.java b/hibernate-core/src/main/java/org/hibernate/engine/query/spi/NativeQueryInterpreterInitiator.java index da55dcb430..b19776a320 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/query/spi/NativeQueryInterpreterInitiator.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/query/spi/NativeQueryInterpreterInitiator.java @@ -21,7 +21,7 @@ public class NativeQueryInterpreterInitiator implements SessionFactoryServiceIni @Override public NativeQueryInterpreter initiateService(SessionFactoryServiceInitiatorContext context) { - return new NativeQueryInterpreterStandardImpl(); + return new NativeQueryInterpreterStandardImpl( context.getSessionFactoryOptions().getNativeJdbcParametersIgnored() ); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/engine/spi/SessionDelegatorBaseImpl.java b/hibernate-core/src/main/java/org/hibernate/engine/spi/SessionDelegatorBaseImpl.java index 27c60a7976..ee55fb4651 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/spi/SessionDelegatorBaseImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/spi/SessionDelegatorBaseImpl.java @@ -240,6 +240,16 @@ public boolean isCriteriaCopyTreeEnabled() { return delegate.isCriteriaCopyTreeEnabled(); } + @Override + public boolean getNativeJdbcParametersIgnored() { + return delegate.getNativeJdbcParametersIgnored(); + } + + @Override + public void setNativeJdbcParametersIgnored(boolean nativeJdbcParametersIgnored) { + delegate.setNativeJdbcParametersIgnored( nativeJdbcParametersIgnored ); + } + @Override public boolean isOpen() { return delegate.isOpen(); diff --git a/hibernate-core/src/main/java/org/hibernate/engine/spi/SharedSessionContractImplementor.java b/hibernate-core/src/main/java/org/hibernate/engine/spi/SharedSessionContractImplementor.java index 3423419d72..9304dba450 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/spi/SharedSessionContractImplementor.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/spi/SharedSessionContractImplementor.java @@ -381,6 +381,10 @@ default String bestGuessEntityName(Object object, EntityEntry entry) { boolean isCriteriaCopyTreeEnabled(); + boolean getNativeJdbcParametersIgnored(); + + void setNativeJdbcParametersIgnored(boolean nativeJdbcParametersIgnored); + /** * Get the current {@link FlushModeType} for this session. *

diff --git a/hibernate-core/src/main/java/org/hibernate/engine/spi/SharedSessionDelegatorBaseImpl.java b/hibernate-core/src/main/java/org/hibernate/engine/spi/SharedSessionDelegatorBaseImpl.java index 0d3fff3f7f..59e4c83cc3 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/spi/SharedSessionDelegatorBaseImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/spi/SharedSessionDelegatorBaseImpl.java @@ -474,6 +474,16 @@ public boolean isCriteriaCopyTreeEnabled() { return delegate.isCriteriaCopyTreeEnabled(); } + @Override + public boolean getNativeJdbcParametersIgnored() { + return delegate.getNativeJdbcParametersIgnored(); + } + + @Override + public void setNativeJdbcParametersIgnored(boolean nativeJdbcParametersIgnored) { + delegate.setNativeJdbcParametersIgnored( nativeJdbcParametersIgnored ); + } + @Override public FlushModeType getFlushMode() { return delegate.getFlushMode(); diff --git a/hibernate-core/src/main/java/org/hibernate/internal/AbstractSharedSessionContract.java b/hibernate-core/src/main/java/org/hibernate/internal/AbstractSharedSessionContract.java index b99765a935..d7d979f57d 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/AbstractSharedSessionContract.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/AbstractSharedSessionContract.java @@ -162,6 +162,8 @@ public abstract class AbstractSharedSessionContract implements SharedSessionCont private boolean criteriaCopyTreeEnabled; + private boolean nativeJdbcParametersIgnored; + protected boolean closed; protected boolean waitingForAutoClose; @@ -183,6 +185,7 @@ public AbstractSharedSessionContract(SessionFactoryImpl factory, SessionCreation sessionEventsManager = createSessionEventsManager(options); entityNameResolver = new CoordinatingEntityNameResolver( factory, interceptor ); setCriteriaCopyTreeEnabled( factory.getSessionFactoryOptions().isCriteriaCopyTreeEnabled() ); + setNativeJdbcParametersIgnored( factory.getSessionFactoryOptions().getNativeJdbcParametersIgnored() ); final StatementInspector statementInspector = interpret( options.getStatementInspector() ); @@ -701,6 +704,16 @@ public boolean isCriteriaCopyTreeEnabled() { return criteriaCopyTreeEnabled; } + @Override + public boolean getNativeJdbcParametersIgnored() { + return nativeJdbcParametersIgnored; + } + + @Override + public void setNativeJdbcParametersIgnored(boolean nativeJdbcParametersIgnored) { + this.nativeJdbcParametersIgnored = nativeJdbcParametersIgnored; + } + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // dynamic HQL handling diff --git a/hibernate-core/src/main/java/org/hibernate/query/sql/internal/NativeQueryImpl.java b/hibernate-core/src/main/java/org/hibernate/query/sql/internal/NativeQueryImpl.java index 63b091f3d0..c5bb244046 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sql/internal/NativeQueryImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sql/internal/NativeQueryImpl.java @@ -173,55 +173,6 @@ else if ( memento.getResultMappingClass() != null ) { ); } - @FunctionalInterface - private interface ResultSetMappingHandler { - boolean resolveResultSetMapping( - ResultSetMapping resultSetMapping, - Consumer querySpaceConsumer, - ResultSetMappingResolutionContext context); - } - - private static ResultSetMapping buildResultSetMapping( - String registeredName, - boolean isDynamic, - SharedSessionContractImplementor session) { - return ResultSetMapping.resolveResultSetMapping( registeredName, isDynamic, session.getFactory() ); - } - - public NativeQueryImpl( - NamedNativeQueryMemento memento, - Supplier resultSetMappingCreator, - ResultSetMappingHandler resultSetMappingHandler, - SharedSessionContractImplementor session) { - super( session ); - - this.originalSqlString = memento.getOriginalSqlString(); - - final ParameterInterpretation parameterInterpretation = resolveParameterInterpretation( - originalSqlString, - session - ); - - this.sqlString = parameterInterpretation.getAdjustedSqlString(); - this.parameterMetadata = parameterInterpretation.toParameterMetadata( session ); - this.parameterOccurrences = parameterInterpretation.getOrderedParameterOccurrences(); - this.parameterBindings = QueryParameterBindingsImpl.from( parameterMetadata, session.getFactory() ); - this.querySpaces = new HashSet<>(); - - this.resultSetMapping = resultSetMappingCreator.get(); - - //noinspection UnnecessaryLocalVariable - final boolean appliedAnyResults = resultSetMappingHandler.resolveResultSetMapping( - resultSetMapping, - querySpaces::add, - this - ); - - this.resultMappingSuppliedToCtor = appliedAnyResults; - - applyOptions( memento ); - } - /** * Constructs a NativeQueryImpl given a sql query defined in the mappings. */ @@ -306,6 +257,40 @@ public NativeQueryImpl( } + public NativeQueryImpl( + NamedNativeQueryMemento memento, + Supplier resultSetMappingCreator, + ResultSetMappingHandler resultSetMappingHandler, + SharedSessionContractImplementor session) { + super( session ); + + this.originalSqlString = memento.getOriginalSqlString(); + + final ParameterInterpretation parameterInterpretation = resolveParameterInterpretation( + originalSqlString, + session + ); + + this.sqlString = parameterInterpretation.getAdjustedSqlString(); + this.parameterMetadata = parameterInterpretation.toParameterMetadata( session ); + this.parameterOccurrences = parameterInterpretation.getOrderedParameterOccurrences(); + this.parameterBindings = QueryParameterBindingsImpl.from( parameterMetadata, session.getFactory() ); + this.querySpaces = new HashSet<>(); + + this.resultSetMapping = resultSetMappingCreator.get(); + + //noinspection UnnecessaryLocalVariable + final boolean appliedAnyResults = resultSetMappingHandler.resolveResultSetMapping( + resultSetMapping, + querySpaces::add, + this + ); + + this.resultMappingSuppliedToCtor = appliedAnyResults; + + applyOptions( memento ); + } + public NativeQueryImpl( String sqlString, NamedResultSetMappingMemento resultSetMappingMemento, @@ -331,6 +316,37 @@ public NativeQueryImpl( this.resultMappingSuppliedToCtor = true; } + public NativeQueryImpl(String sqlString, SharedSessionContractImplementor session) { + super( session ); + + this.querySpaces = new HashSet<>(); + + final ParameterInterpretation parameterInterpretation = resolveParameterInterpretation( sqlString, session ); + this.originalSqlString = sqlString; + this.sqlString = parameterInterpretation.getAdjustedSqlString(); + this.parameterMetadata = parameterInterpretation.toParameterMetadata( session ); + this.parameterOccurrences = parameterInterpretation.getOrderedParameterOccurrences(); + this.parameterBindings = QueryParameterBindingsImpl.from( parameterMetadata, session.getFactory() ); + + this.resultSetMapping = ResultSetMapping.resolveResultSetMapping( sqlString, true, session.getFactory() ); + this.resultMappingSuppliedToCtor = false; + } + + @FunctionalInterface + private interface ResultSetMappingHandler { + boolean resolveResultSetMapping( + ResultSetMapping resultSetMapping, + Consumer querySpaceConsumer, + ResultSetMappingResolutionContext context); + } + + private static ResultSetMapping buildResultSetMapping( + String registeredName, + boolean isDynamic, + SharedSessionContractImplementor session) { + return ResultSetMapping.resolveResultSetMapping( registeredName, isDynamic, session.getFactory() ); + } + public List getParameterOccurrences() { return parameterOccurrences; } @@ -374,22 +390,6 @@ protected void applyOptions(NamedNativeQueryMemento memento) { // todo (6.0) : query returns } - public NativeQueryImpl(String sqlString, SharedSessionContractImplementor session) { - super( session ); - - this.querySpaces = new HashSet<>(); - - final ParameterInterpretation parameterInterpretation = resolveParameterInterpretation( sqlString, session ); - this.originalSqlString = sqlString; - this.sqlString = parameterInterpretation.getAdjustedSqlString(); - this.parameterMetadata = parameterInterpretation.toParameterMetadata( session ); - this.parameterOccurrences = parameterInterpretation.getOrderedParameterOccurrences(); - this.parameterBindings = QueryParameterBindingsImpl.from( parameterMetadata, session.getFactory() ); - - this.resultSetMapping = ResultSetMapping.resolveResultSetMapping( sqlString, true, session.getFactory() ); - this.resultMappingSuppliedToCtor = false; - } - private IllegalArgumentException buildIncompatibleException(Class resultClass, Class actualResultClass) { final String resultClassName = resultClass.getName(); final String actualResultClassName = actualResultClass.getName(); diff --git a/hibernate-core/src/main/java/org/hibernate/query/sql/internal/ParameterParser.java b/hibernate-core/src/main/java/org/hibernate/query/sql/internal/ParameterParser.java index 6cf8540c3d..67a7291ece 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sql/internal/ParameterParser.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sql/internal/ParameterParser.java @@ -47,9 +47,10 @@ private ParameterParser() { * * @param sqlString The string to be parsed/tokenized. * @param recognizer The thing which handles recognition events. + * @param nativeJdbcParametersIgnored Whether to ignore ordinal parameters in native queries or not. * @throws QueryException Indicates unexpected parameter conditions. */ - public static void parse(String sqlString, ParameterRecognizer recognizer) throws QueryException { + public static void parse(String sqlString, ParameterRecognizer recognizer, boolean nativeJdbcParametersIgnored) throws QueryException { checkIsNotAFunctionCall( sqlString ); final int stringLength = sqlString.length(); @@ -163,7 +164,9 @@ else if ( c == '?' ) { } } else { - recognizer.ordinalParameter( indx ); + if ( !nativeJdbcParametersIgnored ) { + recognizer.ordinalParameter( indx ); + } } } else { @@ -175,6 +178,10 @@ else if ( c == '?' ) { recognizer.complete(); } + public static void parse(String sqlString, ParameterRecognizer recognizer) throws QueryException { + parse( sqlString, recognizer, false ); + } + private static void checkIsNotAFunctionCall(String sqlString) { final String trimmed = sqlString.trim(); if ( !( trimmed.startsWith( "{" ) && trimmed.endsWith( "}" ) ) ) { diff --git a/hibernate-core/src/main/java/org/hibernate/query/sql/spi/ParameterRecognizer.java b/hibernate-core/src/main/java/org/hibernate/query/sql/spi/ParameterRecognizer.java index 80c55fbf93..7aa08bffbd 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sql/spi/ParameterRecognizer.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sql/spi/ParameterRecognizer.java @@ -7,7 +7,7 @@ package org.hibernate.query.sql.spi; /** - * Defines the "callback" the process of recognizing native query parameters. + * Defines the "callback" process of recognizing native query parameters. * * @see org.hibernate.engine.query.spi.NativeQueryInterpreter#recognizeParameters */ diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/query/sql/NativeQueryParameterTests.java b/hibernate-core/src/test/java/org/hibernate/orm/test/query/sql/NativeQueryParameterTests.java index 0bf19ec297..6654d18b99 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/query/sql/NativeQueryParameterTests.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/query/sql/NativeQueryParameterTests.java @@ -9,12 +9,15 @@ import java.time.Instant; import java.time.temporal.ChronoUnit; +import org.hibernate.cfg.AvailableSettings; import org.hibernate.type.StandardBasicTypes; import org.hibernate.testing.orm.domain.StandardDomainModel; import org.hibernate.testing.orm.junit.DomainModel; +import org.hibernate.testing.orm.junit.ServiceRegistry; import org.hibernate.testing.orm.junit.SessionFactory; import org.hibernate.testing.orm.junit.SessionFactoryScope; +import org.hibernate.testing.orm.junit.Setting; import org.junit.jupiter.api.Test; /** @@ -34,6 +37,24 @@ public void testBasicParameterBinding(SessionFactoryScope scope) { ); } + @DomainModel( standardModels = StandardDomainModel.HELPDESK ) + @SessionFactory + @ServiceRegistry( + settings = { + @Setting(name = AvailableSettings.NATIVE_IGNORE_JDBC_PARAMETERS, value = "true") + } + ) + @Test + public void testJdbcParameterScanningDisabled(SessionFactoryScope scope) { + scope.inTransaction( + session -> { + // Nonsensical query just to test that the '?' is ignored + session.createNativeQuery( "select t.id, t.ticket_key, t.subject ? from Ticket t where t.ticket_key = :key" ) + .setParameter( "key", "ABC-123" ); + } + ); + } + @Test public void testJpaStylePositionalParametersInNativeSql(SessionFactoryScope scope) { scope.inTransaction( diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/query/sql/ParameterParserTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/query/sql/ParameterParserTest.java index edd483e4c2..0e689103b3 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/query/sql/ParameterParserTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/query/sql/ParameterParserTest.java @@ -6,6 +6,8 @@ */ package org.hibernate.orm.test.query.sql; +import org.hibernate.engine.query.ParameterRecognitionException; +import org.hibernate.engine.query.internal.NativeQueryInterpreterStandardImpl; import org.hibernate.engine.query.spi.ParamLocationRecognizer; import org.hibernate.query.sql.internal.ParameterParser; import org.hibernate.query.sql.spi.ParameterRecognizer; @@ -15,6 +17,7 @@ import static org.hibernate.engine.query.internal.NativeQueryInterpreterStandardImpl.NATIVE_QUERY_INTERPRETER; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; @@ -197,4 +200,44 @@ public void testParseJPAPositionalParameter() { assertEquals( 2, recognizer.getOrdinalParameterDescriptionMap().size() ); } + @Test + public void testJdbcParameterScanningEnabled() { + ParamLocationRecognizer recognizer = createRecognizer(); + + assertThrows( + ParameterRecognitionException.class, + () -> { + NATIVE_QUERY_INTERPRETER.recognizeParameters( + "SELECT column FROM Table WHERE column.id = :param and column.name = ?1", + recognizer + ); + recognizer.validate(); + }, + "Mixed parameter strategies - use just one of named, positional or JPA-ordinal strategy" + ); + } + + @Test + public void testJdbcParameterScanningDisabled() { + ParamLocationRecognizer recognizer = createRecognizer(); + + // Should recognize the jpa style ordinal parameters + new NativeQueryInterpreterStandardImpl( true ).recognizeParameters( + "SELECT column FROM Table WHERE column.id = ?1 and column.name = ?2", + recognizer + ); + recognizer.validate(); + assertEquals( 2, recognizer.getOrdinalParameterDescriptionMap().size() ); + + recognizer = createRecognizer(); + // Should ignore the '?' + new NativeQueryInterpreterStandardImpl( true ).recognizeParameters( + "SELECT column ? FROM Table WHERE column.id = :id", + recognizer + ); + recognizer.validate(); + assertTrue(recognizer.getNamedParameterDescriptionMap().containsKey("id")); + assertEquals( 0, recognizer.getOrdinalParameterDescriptionMap().size() ); + + } } diff --git a/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/validation/MockSessionFactory.java b/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/validation/MockSessionFactory.java index 2f2aa00fce..d8cf100f57 100644 --- a/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/validation/MockSessionFactory.java +++ b/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/validation/MockSessionFactory.java @@ -427,7 +427,7 @@ static Class toPrimitiveClass(Class type) { @Override public NativeQueryInterpreter getNativeQueryInterpreter() { - return new NativeQueryInterpreterStandardImpl(); + return new NativeQueryInterpreterStandardImpl( this.getNativeJdbcParametersIgnored() ); } @Override