Skip to content

Commit 0bdbeb1

Browse files
author
nathan.xu
committed
HHH-16283 - Integrate ParameterMarkerStrategy into NativeQuery
1 parent f474cac commit 0bdbeb1

File tree

5 files changed

+89
-70
lines changed

5 files changed

+89
-70
lines changed

hibernate-core/src/main/java/org/hibernate/query/sql/internal/NativeQueryImpl.java

Lines changed: 58 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@
8888
import org.hibernate.query.sql.spi.ParameterInterpretation;
8989
import org.hibernate.query.sql.spi.ParameterOccurrence;
9090
import org.hibernate.query.sql.spi.SelectInterpretationsKey;
91+
import org.hibernate.sql.ast.spi.ParameterMarkerStrategy;
9192
import org.hibernate.sql.exec.internal.CallbackImpl;
9293
import org.hibernate.sql.exec.spi.Callback;
9394
import org.hibernate.sql.results.jdbc.spi.JdbcValuesMappingProducer;
@@ -368,7 +369,8 @@ private ParameterInterpretation resolveParameterInterpretation(
368369
return interpretationCache.resolveNativeQueryParameters(
369370
sqlString,
370371
s -> {
371-
final ParameterRecognizerImpl parameterRecognizer = new ParameterRecognizerImpl();
372+
final ParameterMarkerStrategy parameterMarkerStrategy = sessionFactory.getServiceRegistry().getService( ParameterMarkerStrategy.class );
373+
final ParameterRecognizerImpl parameterRecognizer = new ParameterRecognizerImpl( parameterMarkerStrategy );
372374

373375
session.getFactory().getServiceRegistry()
374376
.requireService( NativeQueryInterpreter.class )
@@ -734,23 +736,36 @@ protected String expandParameterLists() {
734736
// Some DBs limit number of IN expressions. For now, warn...
735737
final SessionFactoryImplementor sessionFactory = getSessionFactory();
736738
final Dialect dialect = sessionFactory.getJdbcServices().getDialect();
739+
740+
final ParameterMarkerStrategy parameterMarkerStrategy = sessionFactory.getServiceRegistry().getService( ParameterMarkerStrategy.class );
741+
737742
final boolean paddingEnabled = sessionFactory.getSessionFactoryOptions().inClauseParameterPaddingEnabled();
738743
final int inExprLimit = dialect.getInExpressionCountLimit();
739744

740745
StringBuilder sb = null;
746+
StringBuilder occurrenceExpansionSB = null;
741747

742748
// Handle parameter lists
743-
int offset = 0;
744-
for ( ParameterOccurrence occurrence : parameterOccurrences ) {
749+
int sourceOffset = 0;
750+
int expandedParamPosition = 1;
751+
for ( int originalParamPosition = 1; originalParamPosition <= parameterOccurrences.size(); originalParamPosition++ ) {
752+
final ParameterOccurrence occurrence = parameterOccurrences.get( originalParamPosition - 1 );
745753
final QueryParameterImplementor<?> queryParameter = occurrence.getParameter();
746754
final QueryParameterBinding<?> binding = parameterBindings.getBinding( queryParameter );
747755
if ( !binding.isMultiValued() ) {
756+
if ( originalParamPosition != expandedParamPosition ) {
757+
if ( sb == null ) {
758+
sb = new StringBuilder( sqlString );
759+
}
760+
sourceOffset = getNewSourceOffsetAfterReplacement( sb, sourceOffset, occurrence, parameterMarkerStrategy.createMarker( expandedParamPosition, null ) );
761+
}
762+
expandedParamPosition++;
748763
continue;
749764
}
750765
final Collection<?> bindValues = binding.getBindValues();
751766

752-
int bindValueCount = bindValues.size();
753-
int bindValueMaxCount = determineBindValueMaxCount( paddingEnabled, inExprLimit, bindValueCount );
767+
final int bindValueCount = bindValues.size();
768+
final int bindValueMaxCount = determineBindValueMaxCount( paddingEnabled, inExprLimit, bindValueCount );
754769

755770
if ( inExprLimit > 0 && bindValueCount > inExprLimit ) {
756771
log.tooManyInExpressions(
@@ -765,6 +780,7 @@ protected String expandParameterLists() {
765780

766781
final int sourcePosition = occurrence.getSourcePosition();
767782
if ( sourcePosition < 0 ) {
783+
expandedParamPosition++;
768784
continue;
769785
}
770786

@@ -779,7 +795,7 @@ protected String expandParameterLists() {
779795
}
780796
}
781797
if ( isEnclosedInParens ) {
782-
for ( int i = sourcePosition + 1; i < sqlString.length(); i++ ) {
798+
for ( int i = sourcePosition + occurrence.getLength(); i < sqlString.length(); i++ ) {
783799
final char ch = sqlString.charAt( i );
784800
if ( !Character.isWhitespace( ch ) ) {
785801
isEnclosedInParens = ch == ')';
@@ -788,62 +804,62 @@ protected String expandParameterLists() {
788804
}
789805
}
790806

791-
if ( bindValueCount == 1 && isEnclosedInParens ) {
807+
if ( bindValueCount == 1 && isEnclosedInParens && expandedParamPosition == originalParamPosition ) {
792808
// short-circuit for performance when only 1 value and the
793809
// placeholder is already enclosed in parentheses...
810+
expandedParamPosition++;
794811
continue;
795812
}
796813

797814
if ( sb == null ) {
798-
sb = new StringBuilder( sqlString.length() + 20 );
799-
sb.append( sqlString );
815+
sb = new StringBuilder( sqlString );
816+
}
817+
818+
if ( occurrenceExpansionSB == null ) {
819+
occurrenceExpansionSB = new StringBuilder();
820+
}
821+
else {
822+
occurrenceExpansionSB.setLength( 0 );
823+
}
824+
825+
if ( !isEnclosedInParens ) {
826+
occurrenceExpansionSB.append( '(' );
800827
}
801828

802-
final String expansionListAsString;
803829
// HHH-8901
804830
if ( bindValueMaxCount == 0 ) {
805-
if ( isEnclosedInParens ) {
806-
expansionListAsString = "null";
807-
}
808-
else {
809-
expansionListAsString = "(null)";
810-
}
831+
occurrenceExpansionSB.append( "null" );
811832
}
812833
else {
813-
// Shift 1 bit instead of multiplication by 2
814-
char[] chars;
815-
if ( isEnclosedInParens ) {
816-
chars = new char[( bindValueMaxCount << 1 ) - 1];
817-
chars[0] = '?';
818-
for ( int i = 1; i < bindValueMaxCount; i++ ) {
819-
final int index = i << 1;
820-
chars[index - 1] = ',';
821-
chars[index] = '?';
834+
for ( int i = 0; i < bindValueMaxCount; i++ ) {
835+
final String marker = parameterMarkerStrategy.createMarker(
836+
expandedParamPosition + i,
837+
null
838+
);
839+
occurrenceExpansionSB.append( marker );
840+
if ( i + 1 < bindValueMaxCount ) {
841+
occurrenceExpansionSB.append( ',' );
822842
}
823843
}
824-
else {
825-
chars = new char[( bindValueMaxCount << 1 ) + 1];
826-
chars[0] = '(';
827-
chars[1] = '?';
828-
for ( int i = 1; i < bindValueMaxCount; i++ ) {
829-
final int index = i << 1;
830-
chars[index] = ',';
831-
chars[index + 1] = '?';
832-
}
833-
chars[chars.length - 1] = ')';
834-
}
835-
836-
expansionListAsString = new String(chars);
837844
}
845+
if ( !isEnclosedInParens ) {
846+
occurrenceExpansionSB.append( ')' );
847+
}
848+
849+
sourceOffset = getNewSourceOffsetAfterReplacement( sb, sourceOffset, occurrence, occurrenceExpansionSB.toString() );
838850

839-
final int start = sourcePosition + offset;
840-
final int end = start + 1;
841-
sb.replace( start, end, expansionListAsString );
842-
offset += expansionListAsString.length() - 1;
851+
expandedParamPosition += bindValueMaxCount;
843852
}
844853
return sb == null ? sqlString : sb.toString();
845854
}
846855

856+
private int getNewSourceOffsetAfterReplacement(StringBuilder sb, int sourceOffset, ParameterOccurrence occurrence, String replacement) {
857+
final int start = occurrence.getSourcePosition() + sourceOffset;
858+
final int end = start + occurrence.getLength();
859+
sb.replace( start, end, replacement );
860+
return sourceOffset + ( replacement.length() - occurrence.getLength() );
861+
}
862+
847863
public static int determineBindValueMaxCount(boolean paddingEnabled, int inExprLimit, int bindValueCount) {
848864
int bindValueMaxCount = bindValueCount;
849865

hibernate-core/src/main/java/org/hibernate/query/sql/internal/ParameterRecognizerImpl.java

Lines changed: 21 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818
import org.hibernate.query.spi.QueryParameterImplementor;
1919
import org.hibernate.query.sql.spi.ParameterOccurrence;
2020
import org.hibernate.query.sql.spi.ParameterRecognizer;
21+
import org.hibernate.sql.ast.internal.ParameterMarkerStrategyStandard;
22+
import org.hibernate.sql.ast.spi.ParameterMarkerStrategy;
2123

2224
/**
2325
* @author Steve Ebersole
@@ -34,13 +36,15 @@ private enum ParameterStyle {
3436
private Map<String, QueryParameterImplementor<?>> namedQueryParameters;
3537
private Map<Integer, QueryParameterImplementor<?>> positionalQueryParameters;
3638

37-
private int ordinalParameterImplicitPosition;
39+
private int parameterImplicitPosition;
40+
private final ParameterMarkerStrategy parameterMarkerStrategy;
3841

3942
private List<ParameterOccurrence> parameterList;
4043
private final StringBuilder sqlStringBuffer = new StringBuilder();
4144

42-
public ParameterRecognizerImpl() {
43-
ordinalParameterImplicitPosition = 1;
45+
public ParameterRecognizerImpl(ParameterMarkerStrategy parameterMarkerStrategy) {
46+
this.parameterMarkerStrategy = parameterMarkerStrategy == null ? ParameterMarkerStrategyStandard.INSTANCE : parameterMarkerStrategy;
47+
parameterImplicitPosition = 1;
4448
}
4549

4650
@Override
@@ -101,7 +105,7 @@ else if ( parameterStyle != ParameterStyle.JDBC ) {
101105
throw new ParameterRecognitionException( "Cannot mix parameter styles between JDBC-style, ordinal and named in the same query" );
102106
}
103107

104-
int implicitPosition = ordinalParameterImplicitPosition++;
108+
int implicitPosition = parameterImplicitPosition++;
105109

106110
QueryParameterImplementor<?> parameter = null;
107111

@@ -117,12 +121,7 @@ else if ( parameterStyle != ParameterStyle.JDBC ) {
117121
positionalQueryParameters.put( implicitPosition, parameter );
118122
}
119123

120-
if ( parameterList == null ) {
121-
parameterList = new ArrayList<>();
122-
}
123-
124-
parameterList.add( new ParameterOccurrence( parameter, sqlStringBuffer.length() ) );
125-
sqlStringBuffer.append( "?" );
124+
recognizeParameter( parameter, implicitPosition );
126125
}
127126

128127
@Override
@@ -148,12 +147,7 @@ else if ( parameterStyle != ParameterStyle.NAMED ) {
148147
namedQueryParameters.put( name, parameter );
149148
}
150149

151-
if ( parameterList == null ) {
152-
parameterList = new ArrayList<>();
153-
}
154-
155-
parameterList.add( new ParameterOccurrence( parameter, sqlStringBuffer.length() ) );
156-
sqlStringBuffer.append( "?" );
150+
recognizeParameter( parameter, parameterImplicitPosition++ );
157151
}
158152

159153
@Override
@@ -183,16 +177,21 @@ else if ( parameterStyle != ParameterStyle.NAMED ) {
183177
positionalQueryParameters.put( position, parameter );
184178
}
185179

186-
if ( parameterList == null ) {
187-
parameterList = new ArrayList<>();
188-
}
189-
190-
parameterList.add( new ParameterOccurrence( parameter, sqlStringBuffer.length() ) );
191-
sqlStringBuffer.append( "?" );
180+
recognizeParameter( parameter, parameterImplicitPosition++ );
192181
}
193182

194183
@Override
195184
public void other(char character) {
196185
sqlStringBuffer.append( character );
197186
}
187+
188+
private void recognizeParameter(QueryParameterImplementor parameter, int position) {
189+
final String marker = parameterMarkerStrategy.createMarker( position, null );
190+
final int markerLength = marker.length();
191+
if ( parameterList == null ) {
192+
parameterList = new ArrayList<>();
193+
}
194+
sqlStringBuffer.append( marker );
195+
parameterList.add( new ParameterOccurrence( parameter, sqlStringBuffer.length() - markerLength, markerLength ) );
196+
}
198197
}

hibernate-core/src/main/java/org/hibernate/query/sql/spi/ParameterOccurrence.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,12 @@ public final class ParameterOccurrence {
1313

1414
private final QueryParameterImplementor<?> parameter;
1515
private final int sourcePosition;
16+
private final int length;
1617

17-
public ParameterOccurrence(QueryParameterImplementor<?> parameter, int sourcePosition) {
18+
public ParameterOccurrence(QueryParameterImplementor<?> parameter, int sourcePosition, int length) {
1819
this.parameter = parameter;
1920
this.sourcePosition = sourcePosition;
21+
this.length = length;
2022
}
2123

2224
public QueryParameterImplementor<?> getParameter() {
@@ -26,4 +28,8 @@ public QueryParameterImplementor<?> getParameter() {
2628
public int getSourcePosition() {
2729
return sourcePosition;
2830
}
31+
32+
public int getLength() {
33+
return length;
34+
}
2935
}

hibernate-core/src/main/java/org/hibernate/sql/ast/spi/ParameterMarkerStrategy.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
import org.hibernate.type.descriptor.jdbc.JdbcType;
99

1010
/**
11-
* Strategy for generating parameter markers used in {@linkplain java.sql.PreparedStatement preparable} SQL strings.
11+
* Strategy for generating parameter markers used in {@linkplain java.sql.PreparedStatement preparable} and native SQL strings.
1212
* <p/>
1313
* Generally Hibernate will use the JDBC standard marker - {@code ?}. Many JDBC drivers support the
1414
* use of the "native" marker syntax of the underlying database - e.g. {@code $n}, {@code ?n}, ...

hibernate-core/src/test/java/org/hibernate/orm/test/sql/ast/ParameterMarkerStrategyTests.java

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616
import org.hibernate.testing.jdbc.SQLStatementInspector;
1717
import org.hibernate.testing.orm.domain.gambit.EntityOfBasics;
1818
import org.hibernate.testing.orm.junit.DomainModel;
19-
import org.hibernate.testing.orm.junit.FailureExpected;
2019
import org.hibernate.testing.orm.junit.Jira;
2120
import org.hibernate.testing.orm.junit.RequiresDialect;
2221
import org.hibernate.testing.orm.junit.ServiceRegistry;
@@ -125,7 +124,6 @@ public void testMutations(SessionFactoryScope scope) {
125124
}
126125

127126
@Test
128-
@FailureExpected
129127
@Jira( "https://hibernate.atlassian.net/browse/HHH-16283" )
130128
public void testNativeQuery(SessionFactoryScope scope) {
131129
final SQLStatementInspector statementInspector = scope.getCollectingStatementInspector();
@@ -148,8 +146,8 @@ public void testNativeQuery(SessionFactoryScope scope) {
148146
.uniqueResult();
149147

150148
assertThat( statementInspector.getSqlQueries() ).hasSize( 1 );
151-
assertThat( count( statementInspector.getSqlQueries().get( 0 ), "?" ) ).isEqualTo( 1 );
152-
assertThat( statementInspector.getSqlQueries().get( 0 ) ).contains( "?1" );
149+
assertThat( count( statementInspector.getSqlQueries().get( 0 ), "?" ) ).isEqualTo( 2 );
150+
assertThat( statementInspector.getSqlQueries().get( 0 ) ).contains( "?1", "?2" );
153151
} );
154152
}
155153

0 commit comments

Comments
 (0)