HHH-16284 - Rename JdbcParameterRender to ParameterMarkerStrategy

This commit is contained in:
Steve Ebersole 2023-03-13 13:58:25 -05:00
parent f45dcf4c2f
commit b276128f56
29 changed files with 639 additions and 484 deletions

View File

@ -19,7 +19,8 @@ ext {
'jdbc.user' : 'sa', 'jdbc.user' : 'sa',
'jdbc.pass' : '', 'jdbc.pass' : '',
'jdbc.url' : 'jdbc:h2:mem:db1;DB_CLOSE_DELAY=-1;LOCK_TIMEOUT=10000', '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 : [ hsqldb : [
'db.dialect' : 'org.hibernate.dialect.HSQLDialect', 'db.dialect' : 'org.hibernate.dialect.HSQLDialect',

View File

@ -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.query.sqm.mutation.internal.temptable.PersistentTableStrategy;
import org.hibernate.resource.jdbc.spi.PhysicalConnectionHandlingMode; import org.hibernate.resource.jdbc.spi.PhysicalConnectionHandlingMode;
import org.hibernate.resource.jdbc.spi.StatementInspector; import org.hibernate.resource.jdbc.spi.StatementInspector;
import org.hibernate.sql.ast.spi.ParameterMarkerStrategy;
import jakarta.persistence.criteria.CriteriaDelete; import jakarta.persistence.criteria.CriteriaDelete;
import jakarta.persistence.criteria.CriteriaQuery; import jakarta.persistence.criteria.CriteriaQuery;
@ -2789,13 +2790,15 @@ public interface AvailableSettings {
String TIMEZONE_DEFAULT_STORAGE = "hibernate.timezone.default_storage"; 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 * @implNote {@code False} by default, indicating standard JDBC parameter markers (`?`)
* ({@code false}) to use the standard JDBC parameter markers (`?`) instead * 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 ParameterMarkerStrategy
* @see org.hibernate.dialect.Dialect#getNativeParameterRenderer() * @see org.hibernate.dialect.Dialect#getNativeParameterMarkerStrategy()
* *
* @since 6.2 * @since 6.2
*/ */

View File

@ -144,8 +144,8 @@ import org.hibernate.service.spi.ServiceRegistryImplementor;
import org.hibernate.sql.ForUpdateFragment; import org.hibernate.sql.ForUpdateFragment;
import org.hibernate.sql.ast.SqlAstNodeRenderingMode; import org.hibernate.sql.ast.SqlAstNodeRenderingMode;
import org.hibernate.sql.ast.SqlAstTranslatorFactory; import org.hibernate.sql.ast.SqlAstTranslatorFactory;
import org.hibernate.sql.ast.internal.JdbcParameterRendererStandard; import org.hibernate.sql.ast.internal.ParameterMarkerStrategyStandard;
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.SqlAppender;
import org.hibernate.sql.ast.spi.StringBuilderSqlAppender; import org.hibernate.sql.ast.spi.StringBuilderSqlAppender;
import org.hibernate.sql.model.MutationOperation; 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.
* <p/>
* 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() { public ParameterMarkerStrategy getNativeParameterMarkerStrategy() {
return JdbcParameterRendererStandard.INSTANCE; return null;
} }
/** /**

View File

@ -75,7 +75,7 @@ import org.hibernate.query.sqm.sql.SqmTranslatorFactory;
import org.hibernate.service.ServiceRegistry; import org.hibernate.service.ServiceRegistry;
import org.hibernate.service.spi.ServiceRegistryImplementor; import org.hibernate.service.spi.ServiceRegistryImplementor;
import org.hibernate.sql.ast.SqlAstTranslatorFactory; 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.SqlAppender;
import org.hibernate.sql.model.MutationOperation; import org.hibernate.sql.model.MutationOperation;
import org.hibernate.sql.model.internal.OptionalTableUpdate; import org.hibernate.sql.model.internal.OptionalTableUpdate;
@ -193,8 +193,8 @@ public class DialectDelegateWrapper extends Dialect {
} }
@Override @Override
public JdbcParameterRenderer getNativeParameterRenderer() { public ParameterMarkerStrategy getNativeParameterMarkerStrategy() {
return wrapped.getNativeParameterRenderer(); return wrapped.getNativeParameterMarkerStrategy();
} }
@Override @Override

View File

@ -57,7 +57,7 @@ import org.hibernate.service.ServiceRegistry;
import org.hibernate.sql.ast.SqlAstNodeRenderingMode; import org.hibernate.sql.ast.SqlAstNodeRenderingMode;
import org.hibernate.sql.ast.SqlAstTranslator; import org.hibernate.sql.ast.SqlAstTranslator;
import org.hibernate.sql.ast.SqlAstTranslatorFactory; 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.SqlAppender;
import org.hibernate.sql.ast.spi.StandardSqlAstTranslatorFactory; import org.hibernate.sql.ast.spi.StandardSqlAstTranslatorFactory;
import org.hibernate.sql.ast.tree.Statement; import org.hibernate.sql.ast.tree.Statement;
@ -910,18 +910,18 @@ public class H2Dialect extends Dialect {
} }
@Override @Override
public JdbcParameterRenderer getNativeParameterRenderer() { public ParameterMarkerStrategy getNativeParameterMarkerStrategy() {
return OrdinalParameterRenderer.INSTANCE; return OrdinalParameterMarkerStrategy.INSTANCE;
} }
public static class OrdinalParameterRenderer implements JdbcParameterRenderer { public static class OrdinalParameterMarkerStrategy implements ParameterMarkerStrategy {
/** /**
* Singleton access * Singleton access
*/ */
public static final OrdinalParameterRenderer INSTANCE = new OrdinalParameterRenderer(); public static final OrdinalParameterMarkerStrategy INSTANCE = new OrdinalParameterMarkerStrategy();
@Override @Override
public String renderJdbcParameter(int position, JdbcType jdbcType) { public String createMarker(int position, JdbcType jdbcType) {
return "?" + position; return "?" + position;
} }
} }

View File

@ -66,7 +66,7 @@ import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy;
import org.hibernate.service.ServiceRegistry; import org.hibernate.service.ServiceRegistry;
import org.hibernate.sql.ast.SqlAstTranslator; import org.hibernate.sql.ast.SqlAstTranslator;
import org.hibernate.sql.ast.SqlAstTranslatorFactory; 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.SqlAppender;
import org.hibernate.sql.ast.spi.StandardSqlAstTranslatorFactory; import org.hibernate.sql.ast.spi.StandardSqlAstTranslatorFactory;
import org.hibernate.sql.ast.tree.Statement; import org.hibernate.sql.ast.tree.Statement;
@ -145,7 +145,7 @@ public class PostgreSQLDialect extends Dialect {
protected final PostgreSQLDriverKind driverKind; protected final PostgreSQLDriverKind driverKind;
private final OptionalTableUpdateStrategy optionalTableUpdateStrategy; private final OptionalTableUpdateStrategy optionalTableUpdateStrategy;
private final JdbcParameterRenderer parameterRenderer; private final ParameterMarkerStrategy parameterRenderer;
public PostgreSQLDialect() { public PostgreSQLDialect() {
this( MINIMUM_VERSION ); this( MINIMUM_VERSION );
@ -166,7 +166,7 @@ public class PostgreSQLDialect extends Dialect {
this.optionalTableUpdateStrategy = determineOptionalTableUpdateStrategy( version ); this.optionalTableUpdateStrategy = determineOptionalTableUpdateStrategy( version );
this.parameterRenderer = driverKind == PostgreSQLDriverKind.VERT_X this.parameterRenderer = driverKind == PostgreSQLDriverKind.VERT_X
? NativeParameterMarkers.INSTANCE ? NativeParameterMarkers.INSTANCE
: super.getNativeParameterRenderer(); : super.getNativeParameterMarkerStrategy();
} }
private static OptionalTableUpdateStrategy determineOptionalTableUpdateStrategy(DatabaseVersion version) { private static OptionalTableUpdateStrategy determineOptionalTableUpdateStrategy(DatabaseVersion version) {
@ -1433,18 +1433,18 @@ public class PostgreSQLDialect extends Dialect {
} }
@Override @Override
public JdbcParameterRenderer getNativeParameterRenderer() { public ParameterMarkerStrategy getNativeParameterMarkerStrategy() {
return parameterRenderer; return parameterRenderer;
} }
private static class NativeParameterMarkers implements JdbcParameterRenderer { private static class NativeParameterMarkers implements ParameterMarkerStrategy {
/** /**
* Singleton access * Singleton access
*/ */
public static final NativeParameterMarkers INSTANCE = new NativeParameterMarkers(); public static final NativeParameterMarkers INSTANCE = new NativeParameterMarkers();
@Override @Override
public String renderJdbcParameter(int position, JdbcType jdbcType) { public String createMarker(int position, JdbcType jdbcType) {
return "$" + position; return "$" + position;
} }
} }

View File

@ -6129,7 +6129,7 @@ public abstract class AbstractEntityPersister
delete.addColumnRestriction( propertyColumnNames[k] ); delete.addColumnRestriction( propertyColumnNames[k] );
} }
else { else {
delete.addColumnNullnessRestriction( propertyColumnNames[k] ); delete.addColumnIsNullRestriction( propertyColumnNames[k] );
} }
} }
} }

View File

@ -57,9 +57,7 @@ public class NativeNonSelectQueryPlanImpl implements NonSelectQueryPlan {
} }
else { else {
jdbcParameterBinders = new ArrayList<>( parameterList.size() ); jdbcParameterBinders = new ArrayList<>( parameterList.size() );
jdbcParameterBindings = new JdbcParameterBindingsImpl( parameterList.size() ); jdbcParameterBindings = new JdbcParameterBindingsImpl(
jdbcParameterBindings.registerNativeQueryParameters(
queryParameterBindings, queryParameterBindings,
parameterList, parameterList,
jdbcParameterBinders, jdbcParameterBinders,

View File

@ -79,9 +79,7 @@ public class NativeSelectQueryPlanImpl<R> implements NativeSelectQueryPlan<R> {
} }
else { else {
jdbcParameterBinders = new ArrayList<>( parameterList.size() ); jdbcParameterBinders = new ArrayList<>( parameterList.size() );
jdbcParameterBindings = new JdbcParameterBindingsImpl( parameterList.size() ); jdbcParameterBindings = new JdbcParameterBindingsImpl(
jdbcParameterBindings.registerNativeQueryParameters(
queryParameterBindings, queryParameterBindings,
parameterList, parameterList,
jdbcParameterBinders, jdbcParameterBinders,
@ -95,8 +93,7 @@ public class NativeSelectQueryPlanImpl<R> implements NativeSelectQueryPlan<R> {
sql, sql,
jdbcParameterBinders, jdbcParameterBinders,
resultSetMapping, resultSetMapping,
affectedTableNames, affectedTableNames
Collections.emptySet()
); );
final SharedSessionContractImplementor session = executionContext.getSession(); final SharedSessionContractImplementor session = executionContext.getSession();
@ -116,6 +113,7 @@ public class NativeSelectQueryPlanImpl<R> implements NativeSelectQueryPlan<R> {
@Override @Override
public ScrollableResultsImplementor<R> performScroll(ScrollMode scrollMode, DomainQueryExecutionContext executionContext) { public ScrollableResultsImplementor<R> performScroll(ScrollMode scrollMode, DomainQueryExecutionContext executionContext) {
if ( executionContext.getQueryOptions().getEffectiveLimit().getMaxRowsJpa() == 0 ) { if ( executionContext.getQueryOptions().getEffectiveLimit().getMaxRowsJpa() == 0 ) {
//noinspection unchecked
return EmptyScrollableResults.INSTANCE; return EmptyScrollableResults.INSTANCE;
} }
final List<JdbcParameterBinder> jdbcParameterBinders; final List<JdbcParameterBinder> jdbcParameterBinders;
@ -128,9 +126,7 @@ public class NativeSelectQueryPlanImpl<R> implements NativeSelectQueryPlan<R> {
} }
else { else {
jdbcParameterBinders = new ArrayList<>( parameterList.size() ); jdbcParameterBinders = new ArrayList<>( parameterList.size() );
jdbcParameterBindings = new JdbcParameterBindingsImpl( parameterList.size() ); jdbcParameterBindings = new JdbcParameterBindingsImpl(
jdbcParameterBindings.registerNativeQueryParameters(
queryParameterBindings, queryParameterBindings,
parameterList, parameterList,
jdbcParameterBinders, jdbcParameterBinders,
@ -142,8 +138,7 @@ public class NativeSelectQueryPlanImpl<R> implements NativeSelectQueryPlan<R> {
sql, sql,
jdbcParameterBinders, jdbcParameterBinders,
resultSetMapping, resultSetMapping,
affectedTableNames, affectedTableNames
Collections.emptySet()
); );
return executionContext.getSession().getJdbcServices().getJdbcSelectExecutor().scroll( return executionContext.getSession().getJdbcServices().getJdbcSelectExecutor().scroll(

View File

@ -38,7 +38,7 @@ import org.hibernate.query.sqm.mutation.internal.SqmMultiTableMutationStrategyPr
import org.hibernate.resource.beans.spi.ManagedBeanRegistryInitiator; import org.hibernate.resource.beans.spi.ManagedBeanRegistryInitiator;
import org.hibernate.resource.transaction.internal.TransactionCoordinatorBuilderInitiator; import org.hibernate.resource.transaction.internal.TransactionCoordinatorBuilderInitiator;
import org.hibernate.service.internal.SessionFactoryServiceRegistryFactoryInitiator; 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.sql.results.jdbc.internal.JdbcValuesMappingProducerProviderInitiator;
import org.hibernate.tool.schema.internal.SchemaManagementToolInitiator; import org.hibernate.tool.schema.internal.SchemaManagementToolInitiator;
import org.hibernate.tool.schema.internal.script.SqlScriptExtractorInitiator; import org.hibernate.tool.schema.internal.script.SqlScriptExtractorInitiator;
@ -152,8 +152,8 @@ public final class StandardServiceInitiators {
// SqmMultiTableMutationStrategyProvider // SqmMultiTableMutationStrategyProvider
serviceInitiators.add( SqmMultiTableMutationStrategyProviderInitiator.INSTANCE ); serviceInitiators.add( SqmMultiTableMutationStrategyProviderInitiator.INSTANCE );
// JdbcParameterRenderer // ParameterMarkerStrategy
serviceInitiators.add( JdbcParameterRendererInitiator.INSTANCE ); serviceInitiators.add( ParameterMarkerStrategyInitiator.INSTANCE );
serviceInitiators.trimToSize(); serviceInitiators.trimToSize();

View File

@ -16,7 +16,7 @@ import org.hibernate.Internal;
@Internal @Internal
public class ComparisonRestriction implements Restriction { public class ComparisonRestriction implements Restriction {
private final String lhs; private final String lhs;
private final String operator; private final Operator operator;
private final String rhs; private final String rhs;
public ComparisonRestriction(String lhs) { public ComparisonRestriction(String lhs) {
@ -24,10 +24,10 @@ public class ComparisonRestriction implements Restriction {
} }
public ComparisonRestriction(String lhs, String rhs) { 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.lhs = lhs;
this.operator = operator; this.operator = operator;
this.rhs = rhs; this.rhs = rhs;
@ -36,7 +36,7 @@ public class ComparisonRestriction implements Restriction {
@Override @Override
public void render(StringBuilder sqlBuffer, RestrictionRenderingContext context) { public void render(StringBuilder sqlBuffer, RestrictionRenderingContext context) {
sqlBuffer.append( lhs ); sqlBuffer.append( lhs );
sqlBuffer.append( operator ); sqlBuffer.append( operator.getSqlText() );
if ( "?".equals( rhs ) ) { if ( "?".equals( rhs ) ) {
sqlBuffer.append( context.makeParameterMarker() ); sqlBuffer.append( context.makeParameterMarker() );
@ -45,4 +45,20 @@ public class ComparisonRestriction implements Restriction {
sqlBuffer.append( rhs ); sqlBuffer.append( rhs );
} }
} }
public enum Operator {
EQ( "=" ),
NE( "<>" )
;
private final String sqlText;
Operator(String sqlText) {
this.sqlText = sqlText;
}
public String getSqlText() {
return sqlText;
}
}
} }

View File

@ -5,16 +5,14 @@
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>. * See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/ */
package org.hibernate.sql; package org.hibernate.sql;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Map;
import org.hibernate.Internal; import org.hibernate.Internal;
import org.hibernate.dialect.Dialect; import org.hibernate.dialect.Dialect;
import org.hibernate.engine.spi.SessionFactoryImplementor; 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. * A SQL {@code DELETE} statement.
@ -22,24 +20,20 @@ import org.hibernate.sql.ast.spi.JdbcParameterRenderer;
* @author Gavin King * @author Gavin King
*/ */
@Internal @Internal
public class Delete { public class Delete implements RestrictionRenderingContext {
protected String tableName; protected String tableName;
protected String comment; protected String comment;
protected final List<Restriction> restrictions = new ArrayList<>();
protected final List<String> whereFragments = new ArrayList<>(); private final ParameterMarkerStrategy parameterMarkerStrategy;
private final JdbcParameterRenderer jdbcParameterRenderer;
private final boolean standardParamRendering;
private int parameterCount; private int parameterCount;
public Delete(SessionFactoryImplementor factory) { public Delete(SessionFactoryImplementor factory) {
this( factory.getServiceRegistry().getService( JdbcParameterRenderer.class ) ); this( factory.getServiceRegistry().getService( ParameterMarkerStrategy.class ) );
} }
public Delete(JdbcParameterRenderer jdbcParameterRenderer) { public Delete(ParameterMarkerStrategy parameterMarkerStrategy) {
this.jdbcParameterRenderer = jdbcParameterRenderer; this.parameterMarkerStrategy = parameterMarkerStrategy;
this.standardParamRendering = JdbcParameterRenderer.isStandardRenderer( jdbcParameterRenderer );
} }
public Delete setTableName(String tableName) { public Delete setTableName(String tableName) {
@ -54,8 +48,7 @@ public class Delete {
@SuppressWarnings("UnusedReturnValue") @SuppressWarnings("UnusedReturnValue")
public Delete addColumnRestriction(String columnName) { public Delete addColumnRestriction(String columnName) {
final String paramMarker = jdbcParameterRenderer.renderJdbcParameter( ++parameterCount, null ); restrictions.add( new ComparisonRestriction( columnName ) );
this.whereFragments.add( columnName + "=" + paramMarker );
return this; return this;
} }
@ -71,17 +64,14 @@ public class Delete {
} }
@SuppressWarnings("UnusedReturnValue") @SuppressWarnings("UnusedReturnValue")
public Delete addColumnNullnessRestriction(String columnName) { public Delete addColumnIsNullRestriction(String columnName) {
addColumnNullnessRestriction( columnName, false ); restrictions.add( new NullnessRestriction( columnName ) );
return this; return this;
} }
@SuppressWarnings("UnusedReturnValue") @SuppressWarnings("UnusedReturnValue")
public Delete addColumnNullnessRestriction(String columnName, boolean negate) { public Delete addColumnIsNotNullRestriction(String columnName) {
final String fragment = negate restrictions.add( new NullnessRestriction( columnName, false ) );
? columnName + " is not null"
: columnName + " is null";
whereFragments.add( fragment );
return this; return this;
} }
@ -102,25 +92,29 @@ public class Delete {
return buf.toString(); 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) { private void applyComment(StringBuilder buf) {
if ( comment != null ) { if ( comment != null ) {
buf.append( "/* " ).append( Dialect.escapeComment( comment ) ).append( " */ " ); 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 );
}
} }

View File

@ -16,9 +16,8 @@ import org.hibernate.dialect.Dialect;
import org.hibernate.dialect.identity.IdentityColumnSupport; import org.hibernate.dialect.identity.IdentityColumnSupport;
import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.generator.OnExecutionGenerator; 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. * A SQL {@code INSERT} statement.
@ -34,21 +33,19 @@ public class Insert {
protected Map<String,String> columns = new LinkedHashMap<>(); protected Map<String,String> columns = new LinkedHashMap<>();
private final Dialect dialect; private final Dialect dialect;
private final JdbcParameterRenderer jdbcParameterRenderer; private final ParameterMarkerStrategy parameterMarkerStrategy;
private final boolean standardParamRendering;
private int parameterCount; private int parameterCount;
public Insert(SessionFactoryImplementor sessionFactory) { public Insert(SessionFactoryImplementor sessionFactory) {
this( this(
sessionFactory.getJdbcServices().getDialect(), 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.dialect = dialect;
this.jdbcParameterRenderer = jdbcParameterRenderer; this.parameterMarkerStrategy = parameterMarkerStrategy;
this.standardParamRendering = isStandardRenderer( jdbcParameterRenderer );
} }
protected Dialect getDialect() { protected Dialect getDialect() {
@ -179,8 +176,8 @@ public class Insert {
} }
private String normalizeExpressionFragment(String rhs) { private String normalizeExpressionFragment(String rhs) {
return rhs.equals( "?" ) && !standardParamRendering return rhs.equals( "?" )
? jdbcParameterRenderer.renderJdbcParameter( ++parameterCount, null ) ? parameterMarkerStrategy.createMarker( ++parameterCount, null )
: rhs; : rhs;
} }
} }

View File

@ -9,32 +9,32 @@ package org.hibernate.sql;
import org.hibernate.Internal; import org.hibernate.Internal;
/** /**
* Nullness restriction - is [not] null * Nullness restriction - IS (NOT)? NULL
* *
* @author Steve Ebersole * @author Steve Ebersole
*/ */
@Internal @Internal
public class NullnessRestriction implements Restriction { public class NullnessRestriction implements Restriction {
private final String columnName; private final String columnName;
private final boolean negated; private final boolean affirmative;
public NullnessRestriction(String columnName) { 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.columnName = columnName;
this.negated = negated; this.affirmative = affirmative;
} }
@Override @Override
public void render(StringBuilder sqlBuffer, RestrictionRenderingContext context) { public void render(StringBuilder sqlBuffer, RestrictionRenderingContext context) {
sqlBuffer.append( columnName ); sqlBuffer.append( columnName );
if ( negated ) { if ( affirmative ) {
sqlBuffer.append( " is not null" );
}
else {
sqlBuffer.append( " is null" ); sqlBuffer.append( " is null" );
} }
else {
sqlBuffer.append( " is not null" );
}
} }
} }

View File

@ -18,7 +18,7 @@ import org.hibernate.LockMode;
import org.hibernate.LockOptions; import org.hibernate.LockOptions;
import org.hibernate.dialect.Dialect; import org.hibernate.dialect.Dialect;
import org.hibernate.engine.spi.SessionFactoryImplementor; 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. * 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 ); protected LockOptions lockOptions = new LockOptions( LockMode.READ );
private final SessionFactoryImplementor factory;
private final Dialect dialect; private final Dialect dialect;
private final JdbcParameterRenderer jdbcParameterRenderer; private final ParameterMarkerStrategy parameterMarkerStrategy;
private int parameterCount; private int parameterCount;
public SimpleSelect(SessionFactoryImplementor factory) { public SimpleSelect(SessionFactoryImplementor factory) {
this.factory = factory;
this.dialect = factory.getJdbcServices().getDialect(); this.dialect = factory.getJdbcServices().getDialect();
this.jdbcParameterRenderer = factory.getServiceRegistry().getService( JdbcParameterRenderer.class ); this.parameterMarkerStrategy = factory.getServiceRegistry().getService( ParameterMarkerStrategy.class );
} }
@Override @Override
public String makeParameterMarker() { 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 * 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) { public SimpleSelect addRestriction(String columnName) {
restrictions.add( new ComparisonRestriction( 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}. * Appends a restriction based on the comparison between {@code lhs} and {@code rhs}.
* <p/> * <p/>
* 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. * 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 ) ); restrictions.add( new ComparisonRestriction( lhs, op, rhs ) );
return this; return this;
} }

View File

@ -14,7 +14,7 @@ import java.util.Map;
import org.hibernate.Internal; import org.hibernate.Internal;
import org.hibernate.dialect.Dialect; import org.hibernate.dialect.Dialect;
import org.hibernate.engine.spi.SessionFactoryImplementor; 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. * A SQL {@code UPDATE} statement.
@ -28,17 +28,15 @@ public class Update implements RestrictionRenderingContext {
protected Map<String,String> assignments = new LinkedHashMap<>(); protected Map<String,String> assignments = new LinkedHashMap<>();
protected List<Restriction> restrictions = new ArrayList<>(); protected List<Restriction> restrictions = new ArrayList<>();
private final JdbcParameterRenderer jdbcParameterRenderer; private final ParameterMarkerStrategy parameterMarkerStrategy;
private final boolean standardParamRendering;
private int parameterCount; private int parameterCount;
public Update(SessionFactoryImplementor factory) { public Update(SessionFactoryImplementor factory) {
this( factory.getServiceRegistry().getService( JdbcParameterRenderer.class ) ); this( factory.getServiceRegistry().getService( ParameterMarkerStrategy.class ) );
} }
public Update(JdbcParameterRenderer jdbcParameterRenderer) { public Update(ParameterMarkerStrategy parameterMarkerStrategy) {
this.jdbcParameterRenderer = jdbcParameterRenderer; this.parameterMarkerStrategy = parameterMarkerStrategy;
this.standardParamRendering = JdbcParameterRenderer.isStandardRenderer( jdbcParameterRenderer );
} }
public String getTableName() { public String getTableName() {
@ -91,19 +89,26 @@ public class Update implements RestrictionRenderingContext {
return this; 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 ) ); restrictions.add( new ComparisonRestriction( column, op, value ) );
return this; return this;
} }
private String normalizeExpressionFragment(String rhs) { private String normalizeExpressionFragment(String rhs) {
return rhs.equals( "?" ) && !standardParamRendering return rhs.equals( "?" )
? jdbcParameterRenderer.renderJdbcParameter( ++parameterCount, null ) ? parameterMarkerStrategy.createMarker( ++parameterCount, null )
: rhs; : rhs;
} }
public Update addNullnessRestriction(String column) { @SuppressWarnings("UnusedReturnValue")
restrictions.add( new NullnessRestriction( column ) ); 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; return this;
} }
@ -158,6 +163,6 @@ public class Update implements RestrictionRenderingContext {
@Override @Override
public String makeParameterMarker() { public String makeParameterMarker() {
return jdbcParameterRenderer.renderJdbcParameter( ++parameterCount, null ); return parameterMarkerStrategy.createMarker( ++parameterCount, null );
} }
} }

View File

@ -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 "?";
}
}

View File

@ -14,35 +14,39 @@ import org.hibernate.dialect.Dialect;
import org.hibernate.engine.jdbc.spi.JdbcServices; import org.hibernate.engine.jdbc.spi.JdbcServices;
import org.hibernate.internal.util.config.ConfigurationHelper; import org.hibernate.internal.util.config.ConfigurationHelper;
import org.hibernate.service.spi.ServiceRegistryImplementor; import org.hibernate.service.spi.ServiceRegistryImplementor;
import org.hibernate.sql.ast.spi.JdbcParameterRenderer; import org.hibernate.sql.ast.spi.ParameterMarkerStrategy;
/** /**
* @author Steve Ebersole * @author Steve Ebersole
*/ */
public class JdbcParameterRendererInitiator implements StandardServiceInitiator<JdbcParameterRenderer> { public class ParameterMarkerStrategyInitiator implements StandardServiceInitiator<ParameterMarkerStrategy> {
/** /**
* Singleton access * Singleton access
*/ */
public static final JdbcParameterRendererInitiator INSTANCE = new JdbcParameterRendererInitiator(); public static final ParameterMarkerStrategyInitiator INSTANCE = new ParameterMarkerStrategyInitiator();
@Override @Override
public JdbcParameterRenderer initiateService(Map<String, Object> configurationValues, ServiceRegistryImplementor registry) { public ParameterMarkerStrategy initiateService(Map<String, Object> configurationValues, ServiceRegistryImplementor registry) {
final boolean useNativeMarkers = ConfigurationHelper.getBoolean( final boolean useNativeMarkers = ConfigurationHelper.getBoolean(
AvailableSettings.DIALECT_NATIVE_PARAM_MARKERS, AvailableSettings.DIALECT_NATIVE_PARAM_MARKERS,
configurationValues, configurationValues,
true false
); );
if ( useNativeMarkers ) { if ( useNativeMarkers ) {
final Dialect dialect = registry.getService( JdbcServices.class ).getDialect(); 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 @Override
public Class<JdbcParameterRenderer> getServiceInitiated() { public Class<ParameterMarkerStrategy> getServiceInitiated() {
return JdbcParameterRenderer.class; return ParameterMarkerStrategy.class;
} }
} }

View File

@ -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() );
}
}

View File

@ -84,6 +84,7 @@ import org.hibernate.sql.ast.SqlAstJoinType;
import org.hibernate.sql.ast.SqlAstNodeRenderingMode; import org.hibernate.sql.ast.SqlAstNodeRenderingMode;
import org.hibernate.sql.ast.SqlAstTranslator; import org.hibernate.sql.ast.SqlAstTranslator;
import org.hibernate.sql.ast.SqlTreeCreationException; 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.MutationStatement;
import org.hibernate.sql.ast.tree.SqlAstNode; import org.hibernate.sql.ast.tree.SqlAstNode;
import org.hibernate.sql.ast.tree.Statement; import org.hibernate.sql.ast.tree.Statement;
@ -273,7 +274,7 @@ public abstract class AbstractSqlAstTranslator<T extends JdbcOperation> implemen
private JdbcParameterBindings jdbcParameterBindings; private JdbcParameterBindings jdbcParameterBindings;
private Map<JdbcParameter, JdbcParameterBinding> appliedParameterBindings = Collections.emptyMap(); private Map<JdbcParameter, JdbcParameterBinding> appliedParameterBindings = Collections.emptyMap();
private SqlAstNodeRenderingMode parameterRenderingMode = SqlAstNodeRenderingMode.DEFAULT; private SqlAstNodeRenderingMode parameterRenderingMode = SqlAstNodeRenderingMode.DEFAULT;
private final JdbcParameterRenderer jdbcParameterRenderer; private final ParameterMarkerStrategy parameterMarkerStrategy;
private final Stack<Clause> clauseStack = new StandardStack<>( Clause.class ); private final Stack<Clause> clauseStack = new StandardStack<>( Clause.class );
@ -317,7 +318,7 @@ public abstract class AbstractSqlAstTranslator<T extends JdbcOperation> implemen
this.sessionFactory = sessionFactory; this.sessionFactory = sessionFactory;
this.dialect = sessionFactory.getJdbcServices().getDialect(); this.dialect = sessionFactory.getJdbcServices().getDialect();
this.statementStack.push( statement ); this.statementStack.push( statement );
this.jdbcParameterRenderer = sessionFactory.getServiceRegistry().getService( JdbcParameterRenderer.class ); this.parameterMarkerStrategy = sessionFactory.getServiceRegistry().getService( ParameterMarkerStrategy.class );
} }
private static Clause matchWithClause(Clause clause) { private static Clause matchWithClause(Clause clause) {
@ -5073,8 +5074,8 @@ public abstract class AbstractSqlAstTranslator<T extends JdbcOperation> implemen
// If we encounter a plain literal in the select clause which has no literal formatter, we must render it as parameter // If we encounter a plain literal in the select clause which has no literal formatter, we must render it as parameter
if ( literalFormatter == null ) { if ( literalFormatter == null ) {
parameterBinders.add( literal ); parameterBinders.add( literal );
final String marker = parameterMarkerStrategy.createMarker( parameterBinders.size(), literal.getJdbcMapping().getJdbcType() );
final LiteralAsParameter<Object> jdbcParameter = new LiteralAsParameter<>( literal ); final LiteralAsParameter<Object> jdbcParameter = new LiteralAsParameter<>( literal, marker );
if ( castParameter ) { if ( castParameter ) {
renderCasted( jdbcParameter ); renderCasted( jdbcParameter );
} }
@ -6187,14 +6188,21 @@ public abstract class AbstractSqlAstTranslator<T extends JdbcOperation> implemen
jdbcParameters.addParameter( jdbcParameter ); jdbcParameters.addParameter( jdbcParameter );
} }
protected void renderParameterAsParameter(JdbcParameter jdbcParameter) { protected final void renderParameterAsParameter(JdbcParameter jdbcParameter) {
renderParameterAsParameter( jdbcParameter, parameterBinders.size() + 1 );
}
protected void renderParameterAsParameter(JdbcParameter jdbcParameter, int position) {
final JdbcType jdbcType = jdbcParameter.getExpressionType().getJdbcMappings().get( 0 ).getJdbcType(); final JdbcType jdbcType = jdbcParameter.getExpressionType().getJdbcMappings().get( 0 ).getJdbcType();
assert jdbcType != null; 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 ); jdbcType.appendWriteExpression( parameterMarker, this, dialect );
} }
@ -8018,7 +8026,7 @@ public abstract class AbstractSqlAstTranslator<T extends JdbcOperation> implemen
// if there are no parameters or if we are using the standard parameter renderer // if there are no parameters or if we are using the standard parameter renderer
// - the rendering is pretty simple // - the rendering is pretty simple
if ( CollectionHelper.isEmpty( columnWriteFragment.getParameters() ) if ( CollectionHelper.isEmpty( columnWriteFragment.getParameters() )
|| JdbcParameterRenderer.isStandardRenderer( jdbcParameterRenderer ) ) { || ParameterMarkerStrategyStandard.isStandardRenderer( parameterMarkerStrategy ) ) {
simpleColumnWriteFragmentRendering( columnWriteFragment ); simpleColumnWriteFragmentRendering( columnWriteFragment );
return; return;
} }
@ -8034,7 +8042,7 @@ public abstract class AbstractSqlAstTranslator<T extends JdbcOperation> implemen
// to the index of the parameter marker // to the index of the parameter marker
appendSql( sqlFragment.substring( lastEnd, markerStart ) ); appendSql( sqlFragment.substring( lastEnd, markerStart ) );
// render the parameter marker and register it // render the parameter marker and register the parameter handling
visitParameterAsParameter( parameter ); visitParameterAsParameter( parameter );
lastEnd = markerStart + 1; lastEnd = markerStart + 1;

View File

@ -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.
* <p/>
* Generally Hibernate will use the JDBC standard marker - {@code ?}. Many
* databases support alternative marker syntax, often numbered.
* <p/>
* 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() );
}
}

View File

@ -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.
* <p/>
* 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);
}

View File

@ -19,16 +19,17 @@ import org.hibernate.sql.ast.spi.SqlAppender;
* @author Christian Beikov * @author Christian Beikov
*/ */
public class LiteralAsParameter<T> implements SelfRenderingExpression { public class LiteralAsParameter<T> implements SelfRenderingExpression {
private final Literal literal; private final Literal literal;
private final String parameterMarker;
public LiteralAsParameter(Literal literal) { public LiteralAsParameter(Literal literal, String parameterMarker) {
this.literal = literal; this.literal = literal;
this.parameterMarker = parameterMarker;
} }
@Override @Override
public void renderToSql(SqlAppender sqlAppender, SqlAstTranslator<?> walker, SessionFactoryImplementor sessionFactory) { public void renderToSql(SqlAppender sqlAppender, SqlAstTranslator<?> walker, SessionFactoryImplementor sessionFactory) {
sqlAppender.appendSql( "?" ); sqlAppender.append( parameterMarker );
} }
@Override @Override

View File

@ -9,12 +9,25 @@ package org.hibernate.sql.exec.internal;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.IdentityHashMap; import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.function.BiConsumer; 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.ast.tree.expression.JdbcParameter;
import org.hibernate.sql.exec.spi.JdbcParameterBinder;
import org.hibernate.sql.exec.spi.JdbcParameterBinding; import org.hibernate.sql.exec.spi.JdbcParameterBinding;
import org.hibernate.sql.exec.spi.JdbcParameterBindings; import org.hibernate.sql.exec.spi.JdbcParameterBindings;
import org.hibernate.type.BasicTypeReference;
/** /**
* Standard implementation of JdbcParameterBindings * Standard implementation of JdbcParameterBindings
@ -30,6 +43,83 @@ public class JdbcParameterBindingsImpl implements JdbcParameterBindings {
} }
} }
public JdbcParameterBindingsImpl(
QueryParameterBindings queryParameterBindings,
List<ParameterOccurrence> parameterOccurrences,
List<JdbcParameterBinder> 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 @Override
public void addBinding(JdbcParameter parameter, JdbcParameterBinding binding) { public void addBinding(JdbcParameter parameter, JdbcParameterBinding binding) {
if ( bindingMap == null ) { if ( bindingMap == null ) {
@ -47,7 +137,6 @@ public class JdbcParameterBindingsImpl implements JdbcParameterBindings {
@Override @Override
public JdbcParameterBinding getBinding(JdbcParameter parameter) { public JdbcParameterBinding getBinding(JdbcParameter parameter) {
if ( bindingMap == null ) { if ( bindingMap == null ) {
// no bindings
return null; return null;
} }
return bindingMap.get( parameter ); return bindingMap.get( parameter );
@ -55,6 +144,9 @@ public class JdbcParameterBindingsImpl implements JdbcParameterBindings {
@Override @Override
public void visitBindings(BiConsumer<JdbcParameter, JdbcParameterBinding> action) { public void visitBindings(BiConsumer<JdbcParameter, JdbcParameterBinding> action) {
if ( bindingMap == null ) {
return;
}
for ( Map.Entry<JdbcParameter, JdbcParameterBinding> entry : bindingMap.entrySet() ) { for ( Map.Entry<JdbcParameter, JdbcParameterBinding> entry : bindingMap.entrySet() ) {
action.accept( entry.getKey(), entry.getValue() ); action.accept( entry.getKey(), entry.getValue() );
} }

View File

@ -11,34 +11,24 @@ import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.function.BiConsumer; 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.engine.spi.SharedSessionContractImplementor;
import org.hibernate.metamodel.mapping.BasicValuedMapping;
import org.hibernate.metamodel.mapping.Bindable; import org.hibernate.metamodel.mapping.Bindable;
import org.hibernate.metamodel.mapping.JdbcMapping; import org.hibernate.metamodel.mapping.JdbcMapping;
import org.hibernate.query.BindableType;
import org.hibernate.query.internal.BindingTypeHelper; 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.ast.tree.expression.JdbcParameter;
import org.hibernate.sql.exec.internal.JdbcParameterBindingImpl; import org.hibernate.sql.exec.internal.JdbcParameterBindingImpl;
import org.hibernate.sql.exec.internal.JdbcParameterImpl; import org.hibernate.type.spi.TypeConfiguration;
import org.hibernate.type.BasicTypeReference;
/** /**
* 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 * @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 * locate a JdbcParameterBinding. E.g., consider a
* {@link org.hibernate.sql.ast.tree.expression.LiteralAsParameter} * {@link org.hibernate.sql.ast.tree.expression.LiteralAsParameter}
* which actually encapsulates the actually literal value inside * which encapsulates the literal value inside itself - to create the
* itself - to create the binder and actually perform the binding * binder and actually perform the binding is only dependent on the
* is only dependent on the LiteralParameter * LiteralParameter
* *
* @author Steve Ebersole * @author Steve Ebersole
*/ */
@ -90,7 +80,17 @@ public interface JdbcParameterBindings {
offset, offset,
jdbcParameters, jdbcParameters,
session.getFactory().getTypeConfiguration(), session.getFactory().getTypeConfiguration(),
(selectionIndex, params, typeConfiguration, jdbcValue, type) -> { this::createAndAddBinding,
session
);
}
private void createAndAddBinding(
int selectionIndex,
List<JdbcParameter> params,
TypeConfiguration typeConfiguration,
Object jdbcValue,
JdbcMapping type) {
addBinding( addBinding(
params.get( selectionIndex ), params.get( selectionIndex ),
new JdbcParameterBindingImpl( new JdbcParameterBindingImpl(
@ -103,81 +103,4 @@ public interface JdbcParameterBindings {
) )
); );
} }
,
session
);
}
default void registerNativeQueryParameters(
QueryParameterBindings queryParameterBindings,
List<ParameterOccurrence> parameterOccurrences,
List<JdbcParameterBinder> 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;
}
} }

View File

@ -17,6 +17,7 @@ import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.SessionImplementor; import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.persister.collection.CollectionPersister; import org.hibernate.persister.collection.CollectionPersister;
import org.hibernate.persister.collection.QueryableCollection; import org.hibernate.persister.collection.QueryableCollection;
import org.hibernate.sql.ComparisonRestriction;
import org.hibernate.sql.SimpleSelect; import org.hibernate.sql.SimpleSelect;
import org.hibernate.testing.TestForIssue; import org.hibernate.testing.TestForIssue;
@ -76,7 +77,7 @@ public class PersistentListTest {
.setTableName( queryableCollection.getTableName() ) .setTableName( queryableCollection.getTableName() )
.addColumn( "NAME" ) .addColumn( "NAME" )
.addColumn( "LIST_INDEX" ) .addColumn( "LIST_INDEX" )
.addRestriction( "NAME", "<>", "?" ); .addRestriction( "NAME", ComparisonRestriction.Operator.NE, "?" );
PreparedStatement preparedStatement = ( (SessionImplementor) session2 ).getJdbcCoordinator() PreparedStatement preparedStatement = ( (SessionImplementor) session2 ).getJdbcCoordinator()
.getStatementPreparer() .getStatementPreparer()
.prepareStatement( select.toStatementString() ); .prepareStatement( select.toStatementString() );

View File

@ -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;
}
}
}

View File

@ -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;
}
}
}

View File

@ -45,6 +45,7 @@ import org.hibernate.persister.entity.JoinedSubclassEntityPersister;
import org.hibernate.persister.entity.Queryable; import org.hibernate.persister.entity.Queryable;
import org.hibernate.persister.entity.UnionSubclassEntityPersister; import org.hibernate.persister.entity.UnionSubclassEntityPersister;
import org.hibernate.property.access.spi.Getter; import org.hibernate.property.access.spi.Getter;
import org.hibernate.sql.ComparisonRestriction;
import org.hibernate.sql.Update; import org.hibernate.sql.Update;
import org.hibernate.type.BasicType; import org.hibernate.type.BasicType;
import org.hibernate.type.CollectionType; import org.hibernate.type.CollectionType;
@ -580,11 +581,11 @@ public class ValidityAuditStrategy implements AuditStrategy {
// Apply "AND REV <> ?" // Apply "AND REV <> ?"
// todo (PropertyMapping) : need to be able to handle paths // todo (PropertyMapping) : need to be able to handle paths
final String path = configuration.getRevisionNumberPath(); 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 ) ); context.bind( revisionNumber, rootAuditEntity.getPropertyType( path ) );
// Apply "AND REVEND is null" // Apply "AND REVEND is null"
context.addNullnessRestriction( revEndColumnName ); context.addColumnIsNullRestriction( revEndColumnName );
return context; return context;
} }
@ -635,11 +636,11 @@ public class ValidityAuditStrategy implements AuditStrategy {
// Apply "AND REV <> ?" // Apply "AND REV <> ?"
// todo (PropertyMapping) : need to be able to handle paths // 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() ) ); context.bind( revisionNumber, auditEntity.getPropertyType( configuration.getRevisionNumberPath() ) );
// Apply "AND REVEND_TSTMP is null" // Apply "AND REVEND_TSTMP is null"
context.addNullnessRestriction( revEndTimestampColumnName ); context.addColumnIsNullRestriction( revEndTimestampColumnName );
return context; return context;
} }