8000 HHH-19383 - validation of NativeQuery result mappings · hibernate/hibernate-orm@5869a45 · GitHub
[go: up one dir, main page]

Skip to content

Commit 5869a45

Browse files
committed
HHH-19383 - validation of NativeQuery result mappings
Signed-off-by: Jan Schatteman <jschatte@redhat.com>
1 parent 6dc2e7b commit 5869a45

File tree

2 files changed

+56
-14
lines changed

2 files changed

+56
-14
lines changed

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

Lines changed: 26 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -328,24 +328,35 @@ private void handleExplicitResultSetMapping() {
328328
setTupleTransformerForResultType( resultType );
329329
}
330330
else {
331-
checkResultType( resultType );
331+
checkResultType( resultType, resultSetMapping );
332332
}
333333
}
334334
}
335335

336-
private void checkResultType(Class<R> resultType) {
337-
switch ( resultSetMapping.getNumberOfResultBuilders() ) {
338-
case 0:
339-
throw new IllegalArgumentException( "Named query exists, but did not specify a resultClass" );
340-
case 1:
341-
final Class<?> actualResultJavaType =
342-
resultSetMapping.getResultBuilders().get( 0 ).getJavaType();
343-
if ( actualResultJavaType != null && !resultType.isAssignableFrom( actualResultJavaType ) ) {
344-
throw buildIncompatibleException( resultType, actualResultJavaType );
345-
}
346-
break;
347-
default:
348-
throw new IllegalArgumentException( "Cannot create TypedQuery for query with more than one return" );
336+
private void checkResultType(Class<R> resultType, ResultSetMapping resultSetMapping) {
337+
// resultType can be null if any of the deprecated methods were used to create the query
338+
if ( resultType != null && !isResultTypeAlwaysAllowed( resultType )) {
339+
switch ( resultSetMapping.getNumberOfResultBuilders() ) {
340+
case 0:
341+
if ( !resultSetMapping.isDynamic() ) {
342+
throw new IllegalArgumentException( "Named query exists, but did not specify a resultClass" );
343+
}
344+
break;
345+
case 1:
346+
final Class<?> actualResultJavaType =
347+
resultSetMapping.getResultBuilders().get( 0 ).getJavaType();
348+
if ( actualResultJavaType != null && !resultType.isAssignableFrom( actualResultJavaType ) ) {
349+
throw buildIncompatibleException( resultType, actualResultJavaType );
350+
}
351+
break;
352+
default:
353+
for ( ResultBuilder resultBuilder : resultSetMapping.getResultBuilders() ) {
354+
final Class rbJavaType = resultBuilder.getJavaType();
355+
if ( !resultType.isAssignableFrom( rbJavaType ) ) {
356+
throw new IllegalArgumentException( "The result set mapping of the typed query doesn't match the declared return type" );
357+
}
358+
}
359+
}
349360
}
350361
}
351362

@@ -716,6 +727,7 @@ else if ( !isResultTypeAlwaysAllowed( resultType )
716727
else {
717728
mapping = resultSetMapping;
718729
}
730+
checkResultType( resultType, mapping );
719731
return isCacheableQuery()
720732
? getInterpretationCache()
721733
.resolveSelectQueryPlan( selectInterpretationsKey( mapping ), () -> createQueryPlan( mapping ) )

hibernate-core/src/test/java/org/hibernate/orm/test/sql/hand/query/NativeSQLQueriesTest.java

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import org.hibernate.cfg.Environment;
2525
import org.hibernate.dialect.H2Dialect;
2626
import org.hibernate.dialect.MySQLDialect;
27+
import org.hibernate.orm.test.query.NativeQueryLimitOffsetTest;
2728
import org.hibernate.orm.test.sql.hand.Dimension;
2829
import org.hibernate.orm.test.sql.hand.Employment;
2930
import org.hibernate.orm.test.sql.hand.Group;
@@ -38,6 +39,8 @@
3839
import org.hibernate.query.NativeQuery;
3940
import org.hibernate.query.Query;
4041
import org.hibernate.query.ResultListTransformer;
42+
import org.hibernate.testing.orm.junit.Jira;
43+
import org.hibernate.testing.orm.junit.JiraGroup;
4144
import org.hibernate.transform.ResultTransformer;
4245
import org.hibernate.transform.Transformers;
4346
import org.hibernate.type.StandardBasicTypes;
@@ -59,6 +62,7 @@
5962
import static org.junit.jupiter.api.Assertions.assertEquals;
6063
import static org.junit.jupiter.api.Assertions.assertFalse;
6164
import static org.junit.jupiter.api.Assertions.assertNotNull;
65+
import static org.junit.jupiter.api.Assertions.assertThrows;
6266
import static org.junit.jupiter.api.Assertions.assertTrue;
6367
import static org.junit.jupiter.api.Assertions.fail;
6468

@@ -938,6 +942,32 @@ public void testAliasToBeanMap(SessionFactoryScope scope) {
938942
);
939943
}
940944

945+
@Test
946+
@JiraGroup(
947+
{
948+
@Jira("https://hibernate.atlassian.net/browse/HHH-19376"),
949+
@Jira("https://hibernate.atlassian.net/browse/HHH-19383")
950+
}
951+
)
952+
public void testMutateResultSetMapping(SessionFactoryScope scope) {
953+
scope.inTransaction(
954+
session -> {
955+
String sql = "SELECT p.*, COUNT(*) OVER() AS total_count "
956+
+ "FROM Person p "
957+
+ "WHERE p.name ILIKE :name "
958+
+ "ORDER BY p.id";
959+
// Declare Person as result type
960+
NativeQuery<NativeQueryLimitOffsetTest.Person> query = session.createNativeQuery(sql, NativeQueryLimitOffsetTest.Person.class);
961+
query.setParameter("name", "Ja%");
962+
query.setMaxResults(2);
963+
query.setFirstResult(0);
964+
// Now mutate the result set mapping and verify an Exception is thrown
965+
assertThrows( IllegalArgumentException.class,
966+
() -> query.addScalar( "total_count", StandardBasicTypes.LONG).list() );
967+
}
968+
);
969+
}
970+
941971
private String buildLongString(int size, char baseChar) {
942972
StringBuilder buff = new StringBuilder();
943973
for( int i = 0; i < size; i++ ) {

0 commit comments

Comments
 (0)
0