Skip to content

Commit 443b0dc

Browse files
committed
HHH-19472: native queries can return Object[]
1 parent d7c6c6a commit 443b0dc

File tree

3 files changed

+140
-56
lines changed

3 files changed

+140
-56
lines changed
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
/*
2+
* SPDX-License-Identifier: Apache-2.0
3+
* Copyright Red Hat Inc. and Hibernate Authors
4+
*/
5+
package org.hibernate.jpa.spi;
6+
7+
import org.hibernate.query.TupleTransformer;
8+
9+
/**
10+
* A {@link TupleTransformer} for handling {@link Object[]} results from native queries.
11+
*
12+
* @since 7.0
13+
*/
14+
public class NativeQueryArrayTransformer implements TupleTransformer<Object[]> {
15+
16+
public static final NativeQueryArrayTransformer INSTANCE = new NativeQueryArrayTransformer();
17+
18+
@Override
19+
public Object[] transformTuple(Object[] tuple, String[] aliases) {
20+
return tuple;
21+
}
22+
}

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

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import org.hibernate.FlushMode;
2626
import org.hibernate.internal.CoreLogging;
2727
import org.hibernate.internal.CoreMessageLogger;
28+
import org.hibernate.jpa.spi.NativeQueryArrayTransformer;
2829
import org.hibernate.jpa.spi.NativeQueryConstructorTransformer;
2930
import org.hibernate.jpa.spi.NativeQueryListTransformer;
3031
import org.hibernate.jpa.spi.NativeQueryMapTransformer;
@@ -377,7 +378,10 @@ else if ( Map.class.equals( resultClass ) ) {
377378
else if ( List.class.equals( resultClass ) ) {
378379
return NativeQueryListTransformer.INSTANCE;
379380
}
380-
else if ( resultClass != Object.class && resultClass != Object[].class ) {
381+
else if ( Object[].class.equals( resultClass )) {
382+
return NativeQueryArrayTransformer.INSTANCE;
383+
}
384+
else if ( resultClass != Object.class ) {
381385
// TODO: this is extremely fragile and probably a bug
382386
if ( isClass( resultClass ) && !hasJavaTypeDescriptor( resultClass ) ) {
383387
// not a basic type, so something we can attempt

hibernate-core/src/test/java/org/hibernate/orm/test/hql/SingleSelectionArrayResultTest.java

Lines changed: 113 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -4,81 +4,139 @@
44
*/
55
package org.hibernate.orm.test.hql;
66

7+
import java.util.stream.Stream;
8+
9+
import org.hibernate.query.NativeQuery;
10+
import org.hibernate.query.Query;
11+
import org.hibernate.query.SelectionQuery;
12+
713
import org.hibernate.testing.orm.domain.gambit.BasicEntity;
814
import org.hibernate.testing.orm.junit.DomainModel;
915
import org.hibernate.testing.orm.junit.Jira;
1016
import org.hibernate.testing.orm.junit.SessionFactory;
1117
import org.hibernate.testing.orm.junit.SessionFactoryScope;
1218
import org.junit.jupiter.api.AfterAll;
1319
import org.junit.jupiter.api.BeforeAll;
14-
import org.junit.jupiter.api.Test;
20+
import org.junit.jupiter.api.extension.ExtensionContext;
21+
import org.junit.jupiter.params.ParameterizedTest;
22+
import org.junit.jupiter.params.provider.Arguments;
23+
import org.junit.jupiter.params.provider.ArgumentsProvider;
24+
import org.junit.jupiter.params.provider.ArgumentsSource;
1525

1626
import static org.assertj.core.api.Assertions.assertThat;
1727

1828
/**
1929
* @author Marco Belladelli
2030
*/
21-
@DomainModel( annotatedClasses = BasicEntity.class )
31+
@DomainModel(annotatedClasses = BasicEntity.class)
2232
@SessionFactory
23-
@Jira( "https://hibernate.atlassian.net/browse/HHH-18450" )
33+
@Jira("https://hibernate.atlassian.net/browse/HHH-18450")
34+
@Jira("https://hibernate.atlassian.net/browse/HHH-19472")
2435
public class SingleSelectionArrayResultTest {
25-
@Test
26-
public void testArrayResult(SessionFactoryScope scope) {
36+
37+
static class TestArguments implements ArgumentsProvider {
38+
@Override
39+
public Stream<? extends Arguments> provideArguments(ExtensionContext extensionContext) {
40+
return Stream.of(
41+
Arguments.of( "select 1", null, null ),
42+
Arguments.of( "select cast(1 as integer)", null, null ),
43+
Arguments.of( "select id from BasicEntity", null, null ),
44+
Arguments.of( "select cast(id as integer) from BasicEntity", null, null ),
45+
Arguments.of( "select ?1", 1, 1 ),
46+
Arguments.of( "select :p1", "p1", 1 ),
47+
Arguments.of( "select cast(?1 as integer)", 1, 1 ),
48+
Arguments.of( "select cast(:p1 as integer)", "p1", 1 )
49+
);
50+
}
51+
}
52+
53+
@ParameterizedTest
54+
@ArgumentsSource(TestArguments.class)
55+
public void testQueryObjectResult(String ql, Object arg1, Object arg2, SessionFactoryScope scope) {
56+
scope.inTransaction( session -> {
57+
Query<Object> query = session.createQuery( ql, Object.class );
58+
if ( arg1 instanceof Integer ) {
59+
query.setParameter( (Integer) arg1, arg2 );
60+
}
61+
if ( arg1 instanceof String ) {
62+
query.setParameter( (String) arg1, arg2 );
63+
}
64+
assertThat( query.getSingleResult() ).isInstanceOf( Integer.class ).isEqualTo( 1 );
65+
} );
66+
}
67+
68+
@ParameterizedTest
69+
@ArgumentsSource(TestArguments.class)
70+
public void testNativeQueryObjectResult(String ql, Object arg1, Object arg2, SessionFactoryScope scope) {
71+
scope.inTransaction( session -> {
72+
NativeQuery<Object> query = session.createNativeQuery( ql, Object.class );
73+
if ( arg1 instanceof Integer ) {
74+
query.setParameter( (Integer) arg1, arg2 );
75+
}
76+
if ( arg1 instanceof String ) {
77+
query.setParameter( (String) arg1, arg2 );
78+
}
79+
assertThat( query.getSingleResult() ).isInstanceOf( Integer.class ).isEqualTo( 1 );
80+
} );
81+
}
82+
83+
@ParameterizedTest
84+
@ArgumentsSource(TestArguments.class)
85+
public void testSelectionQueryObjectResult(String ql, Object arg1, Object arg2, SessionFactoryScope scope) {
86+
scope.inTransaction( session -> {
87+
SelectionQuery<Object> query = session.createSelectionQuery( ql, Object.class );
88+
if ( arg1 instanceof Integer ) {
89+
query.setParameter( (Integer) arg1, arg2 );
90+
}
91+
if ( arg1 instanceof String ) {
92+
query.setParameter( (String) arg1, arg2 );
93+
}
94+
assertThat( query.getSingleResult() ).isInstanceOf( Integer.class ).isEqualTo( 1 );
95+
} );
96+
}
97+
98+
@ParameterizedTest
99+
@ArgumentsSource(TestArguments.class)
100+
public void testQueryArrayResult(String ql, Object arg1, Object arg2, SessionFactoryScope scope) {
101+
scope.inTransaction( session -> {
102+
Query<Object[]> query = session.createQuery( ql, Object[].class );
103+
if ( arg1 instanceof Integer ) {
104+
query.setParameter( (Integer) arg1, arg2 );
105+
}
106+
if ( arg1 instanceof String ) {
107+
query.setParameter( (String) arg1, arg2 );
108+
}
109+
assertThat( query.getSingleResult() ).containsExactly( 1 );
110+
} );
111+
}
112+
113+
@ParameterizedTest
114+
@ArgumentsSource(TestArguments.class)
115+
public void testNativeQueryArrayResult(String ql, Object arg1, Object arg2, SessionFactoryScope scope) {
27116
scope.inTransaction( session -> {
28-
assertThat( session.createQuery(
29-
"select 1",
30-
Object[].class
31-
).getSingleResult() ).containsExactly( 1 );
32-
assertThat( session.createQuery(
33-
"select cast(1 as integer)",
34-
Object[].class
35-
).getSingleResult() ).containsExactly( 1 );
36-
assertThat( session.createSelectionQuery(
37-
"select id from BasicEntity",
38-
Object[].class
39-
).getSingleResult() ).containsExactly( 1 );
40-
assertThat( session.createSelectionQuery(
41-
"select cast(id as integer) from BasicEntity",
42-
Object[].class
43-
).getSingleResult() ).containsExactly( 1 );
44-
assertThat( session.createSelectionQuery(
45-
"select ?1",
46-
Object[].class
47-
).setParameter( 1, 1 ).getSingleResult() ).containsExactly( 1 );
48-
assertThat( session.createQuery(
49-
"select cast(:p1 as integer)",
50-
Object[].class
51-
).setParameter( "p1", 1 ).getSingleResult() ).containsExactly( 1 );
117+
NativeQuery<Object[]> query = session.createNativeQuery( ql, Object[].class );
118+
if ( arg1 instanceof Integer ) {
119+
query.setParameter( (Integer) arg1, arg2 );
120+
}
121+
if ( arg1 instanceof String ) {
122+
query.setParameter( (String) arg1, arg2 );
123+
}
124+
assertThat( query.getSingleResult() ).containsExactly( 1 );
52125
} );
53126
}
54127

55-
@Test
56-
public void testNormalResult(SessionFactoryScope scope) {
128+
@ParameterizedTest
129+
@ArgumentsSource(TestArguments.class)
130+
public void testSelectionQueryArrayResult(String ql, Object arg1, Object arg2, SessionFactoryScope scope) {
57131
scope.inTransaction( session -> {
58-
assertThat( session.createQuery(
59-
"select 1",
60-
Object.class
61-
).getSingleResult() ).isInstanceOf( Integer.class ).isEqualTo( 1 );
62-
assertThat( session.createQuery(
63-
"select cast(1 as integer)",
64-
Object.class
65-
).getSingleResult() ).isInstanceOf( Integer.class ).isEqualTo( 1 );
66-
assertThat( session.createSelectionQuery(
67-
"select id from BasicEntity",
68-
Object.class
69-
).getSingleResult() ).isInstanceOf( Integer.class ).isEqualTo( 1 );
70-
assertThat( session.createSelectionQuery(
71-
"select cast(id as integer) from BasicEntity",
72-
Object.class
73-
).getSingleResult() ).isInstanceOf( Integer.class ).isEqualTo( 1 );
74-
assertThat( session.createSelectionQuery(
75-
"select ?1",
76-
Object.class
77-
).setParameter( 1, 1 ).getSingleResult() ).isInstanceOf( Integer.class ).isEqualTo( 1 );
78-
assertThat( session.createQuery(
79-
"select cast(:p1 as integer)",
80-
Object.class
81-
).setParameter( "p1", 1 ).getSingleResult() ).isInstanceOf( Integer.class ).isEqualTo( 1 );
132+
SelectionQuery<Object[]> query = session.createSelectionQuery( ql, Object[].class );
133+
if ( arg1 instanceof Integer ) {
134+
query.setParameter( (Integer) arg1, arg2 );
135+
}
136+
if ( arg1 instanceof String ) {
137+
query.setParameter( (String) arg1, arg2 );
138+
}
139+
assertThat( query.getSingleResult() ).containsExactly( 1 );
82140
} );
83141
}
84142

0 commit comments

Comments
 (0)