HHH-16284 - Rename JdbcParameterRender to ParameterMarkerStrategy

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

View File

@ -19,7 +19,8 @@ ext {
'jdbc.user' : 'sa',
'jdbc.pass' : '',
'jdbc.url' : 'jdbc:h2:mem:db1;DB_CLOSE_DELAY=-1;LOCK_TIMEOUT=10000',
'connection.init_sql' : ''
'connection.init_sql' : '',
'hibernate.dialect.native_param_markers' : 'true'
],
hsqldb : [
'db.dialect' : 'org.hibernate.dialect.HSQLDialect',
@ -250,4 +251,4 @@ if ( processTestResourcesTask != null ) {
// processTestResourcesTask.inputs.property( "gradle.libs.versions.hsqldb", project.getProperty( "gradle.libs.versions.hsqldb", "2.7.1" ) )
// processTestResourcesTask.inputs.property( "gradle.libs.versions.derby", project.getProperty( "gradle.libs.versions.derby", "10.15.2.0" ) )
processTestResourcesTask.filter( ReplaceTokens, tokens: dbBundle[db] )
}
}

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.resource.jdbc.spi.PhysicalConnectionHandlingMode;
import org.hibernate.resource.jdbc.spi.StatementInspector;
import org.hibernate.sql.ast.spi.ParameterMarkerStrategy;
import jakarta.persistence.criteria.CriteriaDelete;
import jakarta.persistence.criteria.CriteriaQuery;
@ -2789,13 +2790,15 @@ public interface AvailableSettings {
String TIMEZONE_DEFAULT_STORAGE = "hibernate.timezone.default_storage";
/**
* Controls whether to use JDBC parameter markers (`?`) or dialect native markers.
* Controls whether to use JDBC markers (`?`) or dialect native markers for parameters
* within {@linkplain java.sql.PreparedStatement preparable} SQL statements.
*
* @implNote By default ({@code true}), dialect native markers are used, if any; disable
* ({@code false}) to use the standard JDBC parameter markers (`?`) instead
* @implNote {@code False} by default, indicating standard JDBC parameter markers (`?`)
* are used. Set to {@code true} to use the Dialect's native markers, if any. For
* Dialects without native markers, the standard JDBC strategy is used.
*
* @see org.hibernate.sql.ast.spi.JdbcParameterRenderer
* @see org.hibernate.dialect.Dialect#getNativeParameterRenderer()
* @see ParameterMarkerStrategy
* @see org.hibernate.dialect.Dialect#getNativeParameterMarkerStrategy()
*
* @since 6.2
*/

View File

@ -144,8 +144,8 @@ import org.hibernate.service.spi.ServiceRegistryImplementor;
import org.hibernate.sql.ForUpdateFragment;
import org.hibernate.sql.ast.SqlAstNodeRenderingMode;
import org.hibernate.sql.ast.SqlAstTranslatorFactory;
import org.hibernate.sql.ast.internal.JdbcParameterRendererStandard;
import org.hibernate.sql.ast.spi.JdbcParameterRenderer;
import org.hibernate.sql.ast.internal.ParameterMarkerStrategyStandard;
import org.hibernate.sql.ast.spi.ParameterMarkerStrategy;
import org.hibernate.sql.ast.spi.SqlAppender;
import org.hibernate.sql.ast.spi.StringBuilderSqlAppender;
import org.hibernate.sql.model.MutationOperation;
@ -4815,10 +4815,15 @@ public abstract class Dialect implements ConversionContext, TypeContributor, Fun
}
/**
* Support for native parameter rendering
* Support for native parameter markers.
* <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() {
return JdbcParameterRendererStandard.INSTANCE;
public ParameterMarkerStrategy getNativeParameterMarkerStrategy() {
return null;
}
/**

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -79,9 +79,7 @@ public class NativeSelectQueryPlanImpl<R> implements NativeSelectQueryPlan<R> {
}
else {
jdbcParameterBinders = new ArrayList<>( parameterList.size() );
jdbcParameterBindings = new JdbcParameterBindingsImpl( parameterList.size() );
jdbcParameterBindings.registerNativeQueryParameters(
jdbcParameterBindings = new JdbcParameterBindingsImpl(
queryParameterBindings,
parameterList,
jdbcParameterBinders,
@ -95,8 +93,7 @@ public class NativeSelectQueryPlanImpl<R> implements NativeSelectQueryPlan<R> {
sql,
jdbcParameterBinders,
resultSetMapping,
affectedTableNames,
Collections.emptySet()
affectedTableNames
);
final SharedSessionContractImplementor session = executionContext.getSession();
@ -116,6 +113,7 @@ public class NativeSelectQueryPlanImpl<R> implements NativeSelectQueryPlan<R> {
@Override
public ScrollableResultsImplementor<R> performScroll(ScrollMode scrollMode, DomainQueryExecutionContext executionContext) {
if ( executionContext.getQueryOptions().getEffectiveLimit().getMaxRowsJpa() == 0 ) {
//noinspection unchecked
return EmptyScrollableResults.INSTANCE;
}
final List<JdbcParameterBinder> jdbcParameterBinders;
@ -128,9 +126,7 @@ public class NativeSelectQueryPlanImpl<R> implements NativeSelectQueryPlan<R> {
}
else {
jdbcParameterBinders = new ArrayList<>( parameterList.size() );
jdbcParameterBindings = new JdbcParameterBindingsImpl( parameterList.size() );
jdbcParameterBindings.registerNativeQueryParameters(
jdbcParameterBindings = new JdbcParameterBindingsImpl(
queryParameterBindings,
parameterList,
jdbcParameterBinders,
@ -142,8 +138,7 @@ public class NativeSelectQueryPlanImpl<R> implements NativeSelectQueryPlan<R> {
sql,
jdbcParameterBinders,
resultSetMapping,
affectedTableNames,
Collections.emptySet()
affectedTableNames
);
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.transaction.internal.TransactionCoordinatorBuilderInitiator;
import org.hibernate.service.internal.SessionFactoryServiceRegistryFactoryInitiator;
import org.hibernate.sql.ast.internal.JdbcParameterRendererInitiator;
import org.hibernate.sql.ast.internal.ParameterMarkerStrategyInitiator;
import org.hibernate.sql.results.jdbc.internal.JdbcValuesMappingProducerProviderInitiator;
import org.hibernate.tool.schema.internal.SchemaManagementToolInitiator;
import org.hibernate.tool.schema.internal.script.SqlScriptExtractorInitiator;
@ -152,8 +152,8 @@ public final class StandardServiceInitiators {
// SqmMultiTableMutationStrategyProvider
serviceInitiators.add( SqmMultiTableMutationStrategyProviderInitiator.INSTANCE );
// JdbcParameterRenderer
serviceInitiators.add( JdbcParameterRendererInitiator.INSTANCE );
// ParameterMarkerStrategy
serviceInitiators.add( ParameterMarkerStrategyInitiator.INSTANCE );
serviceInitiators.trimToSize();

View File

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

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

View File

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

View File

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

View File

@ -18,7 +18,7 @@ import org.hibernate.LockMode;
import org.hibernate.LockOptions;
import org.hibernate.dialect.Dialect;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.sql.ast.spi.JdbcParameterRenderer;
import org.hibernate.sql.ast.spi.ParameterMarkerStrategy;
/**
* A SQL {@code SELECT} statement with no table joins.
@ -37,20 +37,18 @@ public class SimpleSelect implements RestrictionRenderingContext {
protected LockOptions lockOptions = new LockOptions( LockMode.READ );
private final SessionFactoryImplementor factory;
private final Dialect dialect;
private final JdbcParameterRenderer jdbcParameterRenderer;
private final ParameterMarkerStrategy parameterMarkerStrategy;
private int parameterCount;
public SimpleSelect(SessionFactoryImplementor factory) {
this.factory = factory;
this.dialect = factory.getJdbcServices().getDialect();
this.jdbcParameterRenderer = factory.getServiceRegistry().getService( JdbcParameterRenderer.class );
this.parameterMarkerStrategy = factory.getServiceRegistry().getService( ParameterMarkerStrategy.class );
}
@Override
public String makeParameterMarker() {
return jdbcParameterRenderer.renderJdbcParameter( ++parameterCount, null );
return parameterMarkerStrategy.createMarker( ++parameterCount, null );
}
/**
@ -104,7 +102,7 @@ public class SimpleSelect implements RestrictionRenderingContext {
/**
* Appends a restriction comparing the {@code columnName} for equality with a parameter
*
* @see #addRestriction(String, String, String)
* @see #addRestriction(String, ComparisonRestriction.Operator, String)
*/
public SimpleSelect addRestriction(String columnName) {
restrictions.add( new ComparisonRestriction( columnName ) );
@ -114,10 +112,10 @@ public class SimpleSelect implements RestrictionRenderingContext {
/**
* Appends a restriction based on the comparison between {@code lhs} and {@code rhs}.
* <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.
*/
public SimpleSelect addRestriction(String lhs, String op, String rhs) {
public SimpleSelect addRestriction(String lhs, ComparisonRestriction.Operator op, String rhs) {
restrictions.add( new ComparisonRestriction( lhs, op, rhs ) );
return this;
}

View File

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

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.internal.util.config.ConfigurationHelper;
import org.hibernate.service.spi.ServiceRegistryImplementor;
import org.hibernate.sql.ast.spi.JdbcParameterRenderer;
import org.hibernate.sql.ast.spi.ParameterMarkerStrategy;
/**
* @author Steve Ebersole
*/
public class JdbcParameterRendererInitiator implements StandardServiceInitiator<JdbcParameterRenderer> {
public class ParameterMarkerStrategyInitiator implements StandardServiceInitiator<ParameterMarkerStrategy> {
/**
* Singleton access
*/
public static final JdbcParameterRendererInitiator INSTANCE = new JdbcParameterRendererInitiator();
public static final ParameterMarkerStrategyInitiator INSTANCE = new ParameterMarkerStrategyInitiator();
@Override
public JdbcParameterRenderer initiateService(Map<String, Object> configurationValues, ServiceRegistryImplementor registry) {
public ParameterMarkerStrategy initiateService(Map<String, Object> configurationValues, ServiceRegistryImplementor registry) {
final boolean useNativeMarkers = ConfigurationHelper.getBoolean(
AvailableSettings.DIALECT_NATIVE_PARAM_MARKERS,
configurationValues,
true
false
);
if ( useNativeMarkers ) {
final Dialect dialect = registry.getService( JdbcServices.class ).getDialect();
return dialect.getNativeParameterRenderer();
final ParameterMarkerStrategy nativeParameterMarkerStrategy = dialect.getNativeParameterMarkerStrategy();
// the Dialect may return `null`, indicating falling-through to the standard strategy
if ( nativeParameterMarkerStrategy != null ) {
return nativeParameterMarkerStrategy;
}
}
return JdbcParameterRendererStandard.INSTANCE;
return ParameterMarkerStrategyStandard.INSTANCE;
}
@Override
public Class<JdbcParameterRenderer> getServiceInitiated() {
return JdbcParameterRenderer.class;
public Class<ParameterMarkerStrategy> getServiceInitiated() {
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.SqlAstTranslator;
import org.hibernate.sql.ast.SqlTreeCreationException;
import org.hibernate.sql.ast.internal.ParameterMarkerStrategyStandard;
import org.hibernate.sql.ast.tree.MutationStatement;
import org.hibernate.sql.ast.tree.SqlAstNode;
import org.hibernate.sql.ast.tree.Statement;
@ -273,7 +274,7 @@ public abstract class AbstractSqlAstTranslator<T extends JdbcOperation> implemen
private JdbcParameterBindings jdbcParameterBindings;
private Map<JdbcParameter, JdbcParameterBinding> appliedParameterBindings = Collections.emptyMap();
private SqlAstNodeRenderingMode parameterRenderingMode = SqlAstNodeRenderingMode.DEFAULT;
private final JdbcParameterRenderer jdbcParameterRenderer;
private final ParameterMarkerStrategy parameterMarkerStrategy;
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.dialect = sessionFactory.getJdbcServices().getDialect();
this.statementStack.push( statement );
this.jdbcParameterRenderer = sessionFactory.getServiceRegistry().getService( JdbcParameterRenderer.class );
this.parameterMarkerStrategy = sessionFactory.getServiceRegistry().getService( ParameterMarkerStrategy.class );
}
private static Clause matchWithClause(Clause clause) {
@ -5073,8 +5074,8 @@ public abstract class AbstractSqlAstTranslator<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 ( literalFormatter == null ) {
parameterBinders.add( literal );
final LiteralAsParameter<Object> jdbcParameter = new LiteralAsParameter<>( literal );
final String marker = parameterMarkerStrategy.createMarker( parameterBinders.size(), literal.getJdbcMapping().getJdbcType() );
final LiteralAsParameter<Object> jdbcParameter = new LiteralAsParameter<>( literal, marker );
if ( castParameter ) {
renderCasted( jdbcParameter );
}
@ -6187,14 +6188,21 @@ public abstract class AbstractSqlAstTranslator<T extends JdbcOperation> implemen
jdbcParameters.addParameter( jdbcParameter );
}
protected void renderParameterAsParameter(JdbcParameter jdbcParameter) {
renderParameterAsParameter( jdbcParameter, parameterBinders.size() + 1 );
}
protected void renderParameterAsParameter(JdbcParameter jdbcParameter, int position) {
protected final void renderParameterAsParameter(JdbcParameter jdbcParameter) {
final JdbcType jdbcType = jdbcParameter.getExpressionType().getJdbcMappings().get( 0 ).getJdbcType();
assert jdbcType != null;
final String parameterMarker = jdbcParameterRenderer.renderJdbcParameter( position, jdbcType );
renderParameterAsParameter( parameterBinders.size() + 1, jdbcParameter );
}
/**
* Renders a parameter marker for the given position
* @param jdbcParameter
* @param position
*/
protected void renderParameterAsParameter(int position, JdbcParameter jdbcParameter) {
final JdbcType jdbcType = jdbcParameter.getExpressionType().getJdbcMappings().get( 0 ).getJdbcType();
assert jdbcType != null;
final String parameterMarker = parameterMarkerStrategy.createMarker( position, jdbcType );
jdbcType.appendWriteExpression( parameterMarker, this, dialect );
}
@ -8018,7 +8026,7 @@ public abstract class AbstractSqlAstTranslator<T extends JdbcOperation> implemen
// if there are no parameters or if we are using the standard parameter renderer
// - the rendering is pretty simple
if ( CollectionHelper.isEmpty( columnWriteFragment.getParameters() )
|| JdbcParameterRenderer.isStandardRenderer( jdbcParameterRenderer ) ) {
|| ParameterMarkerStrategyStandard.isStandardRenderer( parameterMarkerStrategy ) ) {
simpleColumnWriteFragmentRendering( columnWriteFragment );
return;
}
@ -8034,7 +8042,7 @@ public abstract class AbstractSqlAstTranslator<T extends JdbcOperation> implemen
// to the index of the parameter marker
appendSql( sqlFragment.substring( lastEnd, markerStart ) );
// render the parameter marker and register it
// render the parameter marker and register the parameter handling
visitParameterAsParameter( parameter );
lastEnd = markerStart + 1;

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
*/
public class LiteralAsParameter<T> implements SelfRenderingExpression {
private final Literal literal;
private final String parameterMarker;
public LiteralAsParameter(Literal literal) {
public LiteralAsParameter(Literal literal, String parameterMarker) {
this.literal = literal;
this.parameterMarker = parameterMarker;
}
@Override
public void renderToSql(SqlAppender sqlAppender, SqlAstTranslator<?> walker, SessionFactoryImplementor sessionFactory) {
sqlAppender.appendSql( "?" );
sqlAppender.append( parameterMarker );
}
@Override

View File

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

View File

@ -11,34 +11,24 @@ import java.util.Collections;
import java.util.List;
import java.util.function.BiConsumer;
import org.hibernate.dialect.Dialect;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.metamodel.mapping.BasicValuedMapping;
import org.hibernate.metamodel.mapping.Bindable;
import org.hibernate.metamodel.mapping.JdbcMapping;
import org.hibernate.query.BindableType;
import org.hibernate.query.internal.BindingTypeHelper;
import org.hibernate.query.spi.QueryParameterBinding;
import org.hibernate.query.spi.QueryParameterBindings;
import org.hibernate.query.spi.QueryParameterImplementor;
import org.hibernate.query.sql.internal.NativeQueryImpl;
import org.hibernate.query.sql.spi.ParameterOccurrence;
import org.hibernate.sql.ast.tree.expression.JdbcParameter;
import org.hibernate.sql.exec.internal.JdbcParameterBindingImpl;
import org.hibernate.sql.exec.internal.JdbcParameterImpl;
import org.hibernate.type.BasicTypeReference;
import org.hibernate.type.spi.TypeConfiguration;
/**
* Access to all of the externalized JDBC parameter bindings
* Access to all the externalized JDBC parameter bindings
*
* @apiNote "Externalized" because some JDBC parameter values are
* intrinsically part of the parameter itself and we do not need to
* intrinsically part of the parameter itself, and we do not need to
* locate a JdbcParameterBinding. E.g., consider a
* {@link org.hibernate.sql.ast.tree.expression.LiteralAsParameter}
* which actually encapsulates the actually literal value inside
* itself - to create the binder and actually perform the binding
* is only dependent on the LiteralParameter
* which encapsulates the literal value inside itself - to create the
* binder and actually perform the binding is only dependent on the
* LiteralParameter
*
* @author Steve Ebersole
*/
@ -90,94 +80,27 @@ public interface JdbcParameterBindings {
offset,
jdbcParameters,
session.getFactory().getTypeConfiguration(),
(selectionIndex, params, typeConfiguration, jdbcValue, type) -> {
addBinding(
params.get( selectionIndex ),
new JdbcParameterBindingImpl(
BindingTypeHelper.INSTANCE.resolveBindType(
jdbcValue,
type,
typeConfiguration
),
jdbcValue
)
);
}
,
this::createAndAddBinding,
session
);
}
default void registerNativeQueryParameters(
QueryParameterBindings queryParameterBindings,
List<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;
private void createAndAddBinding(
int selectionIndex,
List<JdbcParameter> params,
TypeConfiguration typeConfiguration,
Object jdbcValue,
JdbcMapping type) {
addBinding(
params.get( selectionIndex ),
new JdbcParameterBindingImpl(
BindingTypeHelper.INSTANCE.resolveBindType(
jdbcValue,
type,
typeConfiguration
),
jdbcValue
)
);
}
}

View File

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

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