Skip to content

Commit c4f8427

Browse files
author
nathan.xu
committed
HHH-16283 - refactor testing case into its own class to go about more thorough validation
1 parent e4d3431 commit c4f8427

File tree

4 files changed

+184
-33
lines changed

4 files changed

+184
-33
lines changed

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
@@ -10,7 +10,7 @@
1010
import org.hibernate.type.descriptor.jdbc.JdbcType;
1111

1212
/**
13-
* Strategy for generating parameter markers used in {@linkplain java.sql.PreparedStatement preparable} SQL strings.
13+
* Strategy for generating parameter markers used in {@linkplain java.sql.PreparedStatement preparable} and native SQL strings.
1414
* <p/>
1515
* Generally Hibernate will use the JDBC standard marker - {@code ?}. Many JDBC drivers support the
1616
* use of the "native" marker syntax of the underlying database - e.g. {@code $n}, {@code ?n}, ...
Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
package org.hibernate.orm.test.sql.ast;
2+
3+
import java.util.List;
4+
5+
import org.hibernate.query.NativeQuery;
6+
import org.hibernate.query.sql.spi.NativeQueryImplementor;
7+
import org.hibernate.sql.ast.spi.ParameterMarkerStrategy;
8+
import org.hibernate.type.descriptor.jdbc.JdbcType;
9+
10+
import org.hibernate.testing.jdbc.SQLStatementInspector;
11+
import org.hibernate.testing.orm.junit.DialectContext;
12+
import org.hibernate.testing.orm.junit.DialectFeatureChecks;
13+
import org.hibernate.testing.orm.junit.DomainModel;
14+
import org.hibernate.testing.orm.junit.Jira;
15+
import org.hibernate.testing.orm.junit.RequiresDialectFeature;
16+
import org.hibernate.testing.orm.junit.ServiceRegistry;
17+
import org.hibernate.testing.orm.junit.SessionFactory;
18+
import org.hibernate.testing.orm.junit.SessionFactoryScope;
19+
import org.hibernate.testing.orm.junit.SessionFactoryScopeAware;
20+
import org.junit.jupiter.params.ParameterizedTest;
21+
import org.junit.jupiter.params.provider.EnumSource;
22+
23+
import jakarta.persistence.Entity;
24+
import jakarta.persistence.Id;
25+
import jakarta.persistence.Table;
26+
27+
import static org.assertj.core.api.Assertions.assertThat;
28+
29+
/**
30+
* @author Nathan Xu
31+
*/
32+
@ServiceRegistry( services = @ServiceRegistry.Service(
33+
role = ParameterMarkerStrategy.class,
34+
impl = NativeParameterMarkerStrategyTests.DialectParameterMarkerStrategy.class
35+
) )
36+
@DomainModel( annotatedClasses = NativeParameterMarkerStrategyTests.Book.class )
37+
@SessionFactory( useCollectingStatementInspector = true )
38+
@RequiresDialectFeature( feature = DialectFeatureChecks.SupportsNativeParameterMarker.class )
39+
@Jira( "https://hibernate.atlassian.net/browse/HHH-16283" )
40+
class NativeParameterMarkerStrategyTests implements SessionFactoryScopeAware {
41+
42+
private enum ParameterStyle {
43+
JDBC,
44+
ORDINAL,
45+
NAMED
46+
}
47+
48+
public static class DialectParameterMarkerStrategy implements ParameterMarkerStrategy {
49+
@Override
50+
public String createMarker(int position, JdbcType jdbcType) {
51+
return DialectContext.getDialect().getNativeParameterMarkerStrategy().createMarker( position, jdbcType );
52+
}
53+
}
54+
55+
private SessionFactoryScope scope;
56+
57+
@Override
58+
public void injectSessionFactoryScope(SessionFactoryScope scope) {
59+
this.scope = scope;
60+
}
61+
62+
@ParameterizedTest
63+
@EnumSource(ParameterStyle.class)
64+
void test_happy_path(ParameterStyle style) {
65+
final SQLStatementInspector statementInspector = scope.getCollectingStatementInspector();
66+
statementInspector.clear();
67+
68+
scope.inTransaction( (session) -> {
69+
final NativeQueryImplementor nativeQuery;
70+
final var parameterValue = "War and Peace";
71+
if (style == ParameterStyle.NAMED) {
72+
nativeQuery = session.createNativeQuery( "select * from books b where b.title = :title", Book.class )
73+
.setParameter( "title", parameterValue);
74+
} else {
75+
nativeQuery = session.createNativeQuery( "select * from books b where b.title = " + (style == ParameterStyle.ORDINAL ? "?1" : "?"), Book.class )
76+
.setParameter( 1, parameterValue );
77+
};
78+
nativeQuery.uniqueResult();
79+
assertNativeQueryContainsMarkers( statementInspector, 1 );
80+
} );
81+
}
82+
83+
@ParameterizedTest
84+
@EnumSource(ParameterStyle.class)
85+
void test_parameter_expansion(ParameterStyle style) {
86+
final SQLStatementInspector statementInspector = scope.getCollectingStatementInspector();
87+
statementInspector.clear();
88+
89+
final var parameterValue = List.of("Moby-Dick", "Don Quixote", "In Search of Lost Time");
90+
91+
scope.inTransaction( (session) -> {
92+
final NativeQuery nativeQuery;
93+
if (style == ParameterStyle.NAMED) {
94+
nativeQuery = session.createNativeQuery( "select * from books b where b.title in :titles", Book.class )
95+
.setParameterList( "titles", parameterValue );
96+
} else {
97+
nativeQuery = session.createNativeQuery( "select * from books b where b.title in " + (style == ParameterStyle.ORDINAL ? "?1" : "?"), Book.class )
98+
.setParameterList( 1, parameterValue );
99+
};
100+
nativeQuery.list();
101+
assertNativeQueryContainsMarkers( statementInspector, parameterValue.size() );
102+
} );
103+
}
104+
105+
@ParameterizedTest
106+
@EnumSource(ParameterStyle.class)
107+
void test_limit_handler(ParameterStyle style) {
108+
final SQLStatementInspector statementInspector = scope.getCollectingStatementInspector();
109+
statementInspector.clear();
110+
111+
scope.inTransaction( (session) -> {
112+
final NativeQueryImplementor nativeQuery;
113+
final var parameterValue = "Herman Melville";
114+
if (style == ParameterStyle.NAMED) {
115+
nativeQuery = session.createNativeQuery( "select * from books b where b.author = :author", Book.class )
116+
.setParameter( "author", parameterValue );
117+
} else {
118+
nativeQuery = session.createNativeQuery( "select * from books b where b.author = " + (style == ParameterStyle.ORDINAL ? "?1" : "?"), Book.class )
119+
.setParameter( 1, parameterValue );
120+
};
121+
nativeQuery.setFirstResult( 2 ).setMaxResults( 1 ).list();
122+
123+
assertNativeQueryContainsMarkers( statementInspector, 3 );
124+
} );
125+
}
126+
127+
@ParameterizedTest
128+
@EnumSource(ParameterStyle.class)
129+
void test_parameter_expansion_and_limit_handler(ParameterStyle style) {
130+
final SQLStatementInspector statementInspector = scope.getCollectingStatementInspector();
131+
statementInspector.clear();
132+
133+
final var parameterValue = List.of("Moby-Dick", "Don Quixote", "In Search of Lost Time");
134+
135+
scope.inTransaction( (session) -> {
136+
final NativeQueryImplementor nativeQuery;
137+
if (style == ParameterStyle.NAMED) {
138+
nativeQuery = session.createNativeQuery( "select * from books b where b.title in :titles", Book.class )
139+
.setParameterList( "titles", parameterValue );
140+
} else {
141+
nativeQuery = session.createNativeQuery( "select * from books b where b.title in " + (style == ParameterStyle.ORDINAL ? "?1" : "?"), Book.class )
142+
.setParameterList( 1, parameterValue );
143+
};
144+
nativeQuery.setFirstResult( 1 ).setMaxResults( 3 ).list();
145+
146+
assertNativeQueryContainsMarkers( statementInspector, parameterValue.size() + 2 );
147+
} );
148+
}
149+
150+
private void assertNativeQueryContainsMarkers(SQLStatementInspector statementInspector, int expectedMarkerNum) {
151+
152+
final var strategy = new DialectParameterMarkerStrategy();
153+
154+
final var expectedMarkers = new String[expectedMarkerNum];
155+
for (int i = 1; i <= expectedMarkerNum; i++) {
156+
expectedMarkers[i - 1] = strategy.createMarker( i, null );
157+
}
158+
159+
final var unexpectedMarker = strategy.createMarker( expectedMarkerNum + 1, null );
160+
161+
assertThat( statementInspector.getSqlQueries() )
162+
.singleElement()
163+
.satisfies( query -> assertThat( query ).contains( expectedMarkers ).doesNotContain( unexpectedMarker ) );
164+
}
165+
166+
@Entity
167+
@Table(name = "books")
168+
static class Book {
169+
@Id
170+
String isbn;
171+
String title;
172+
String author;
173+
}
174+
175+
}

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

Lines changed: 1 addition & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,6 @@
66
*/
77
package org.hibernate.orm.test.sql.ast;
88

9-
import java.util.List;
10-
119
import org.hibernate.LockMode;
1210
import org.hibernate.annotations.Filter;
1311
import org.hibernate.annotations.FilterDef;
@@ -34,7 +32,7 @@
3432
import jakarta.persistence.Version;
3533

3634
import static org.assertj.core.api.Assertions.assertThat;
37-
import static org.hibernate.internal.util.StringHelper.*;
35+
import static org.hibernate.internal.util.StringHelper.count;
3836

3937
/**
4038
* @implNote Restricted to H2 as there is nothing intrinsically Dialect specific here,
@@ -148,35 +146,6 @@ public void testLocking(SessionFactoryScope scope) {
148146
} );
149147
}
150148

151-
@Test
152-
@Jira( "https://hibernate.atlassian.net/browse/HHH-16283" )
153-
public void testNativeQuery(SessionFactoryScope scope) {
154-
final SQLStatementInspector statementInspector = scope.getCollectingStatementInspector();
155-
156-
statementInspector.clear();
157-
scope.inTransaction( (session) -> {
158-
session.createNativeQuery( "select count(1) from filtered_entity e where e.region = :region" )
159-
.setParameter( "region", "ABC" )
160-
.uniqueResult();
161-
162-
assertThat( statementInspector.getSqlQueries() ).hasSize( 1 );
163-
assertThat( count( statementInspector.getSqlQueries().get( 0 ), "?" ) ).isEqualTo( 1 );
164-
assertThat( statementInspector.getSqlQueries().get( 0 ) ).contains( "?1" );
165-
} );
166-
167-
statementInspector.clear();
168-
scope.inTransaction( (session) -> {
169-
session.createNativeQuery( "select count(1) from filtered_entity e where e.region in (:region)" )
170-
.setParameterList( "region", List.of( "ABC", "DEF" ) )
171-
.uniqueResult();
172-
173-
assertThat( statementInspector.getSqlQueries() ).hasSize( 1 );
174-
assertThat( count( statementInspector.getSqlQueries().get( 0 ), "?" ) ).isEqualTo( 2 );
175-
assertThat( statementInspector.getSqlQueries().get( 0 ) ).contains( "?1" );
176-
assertThat( statementInspector.getSqlQueries().get( 0 ) ).contains( "?2" );
177-
} );
178-
}
179-
180149
@AfterEach
181150
public void cleanUpTestData(SessionFactoryScope scope) {
182151
scope.inTransaction( (session) -> {

hibernate-testing/src/main/java/org/hibernate/testing/orm/junit/DialectFeatureChecks.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -699,4 +699,11 @@ public boolean apply(Dialect dialect) {
699699
return dialect.getNationalizationSupport() == NationalizationSupport.EXPLICIT;
700700
}
701701
}
702+
703+
public static class SupportsNativeParameterMarker implements DialectFeatureCheck {
704+
@Override
705+
public boolean apply(Dialect dialect) {
706+
return dialect.getNativeParameterMarkerStrategy() != null;
707+
}
708+
}
702709
}

0 commit comments

Comments
 (0)