Skip to content

Commit 18c70ee

Browse files
committed
HHH-19472: native queries can return Object[]
1 parent bb931c7 commit 18c70ee

File tree

3 files changed

+141
-56
lines changed

3 files changed

+141
-56
lines changed
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
/*
2+
* Hibernate, Relational Persistence for Idiomatic Java
3+
*
4+
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
5+
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
6+
*/
7+
package org.hibernate.jpa.spi;
8+
9+
import org.hibernate.query.TupleTransformer;
10+
11+
/**
12+
* A {@link TupleTransformer} for handling {@code Object[]} results from native queries.
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
@@ -23,6 +23,7 @@
2323
import org.checkerframework.checker.nullness.qual.Nullable;
2424
import org.hibernate.CacheMode;
2525
import org.hibernate.FlushMode;
26+
import org.hibernate.jpa.spi.NativeQueryArrayTransformer;
2627
import org.hibernate.jpa.spi.NativeQueryConstructorTransformer;
2728
import org.hibernate.jpa.spi.NativeQueryListTransformer;
2829
import org.hibernate.jpa.spi.NativeQueryMapTransformer;
@@ -384,7 +385,10 @@ else if ( Map.class.equals( resultClass ) ) {
384385
else if ( List.class.equals( resultClass ) ) {
385386
return NativeQueryListTransformer.INSTANCE;
386387
}
387-
else if ( resultClass != Object.class && resultClass != Object[].class ) {
388+
else if ( Object[].class.equals( resultClass )) {
389+
return NativeQueryArrayTransformer.INSTANCE;
390+
}
391+
else if ( resultClass != Object.class ) {
388392
if ( isClass( resultClass ) && !hasJavaTypeDescriptor( resultClass ) ) {
389393
// not a basic type
390394
return new NativeQueryConstructorTransformer<>( resultClass );

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

Lines changed: 114 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -4,81 +4,140 @@
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")
35+
@RequiresDialects({@RequiresDialect(H2Dialect.class), @RequiresDialect(PostgreSQLDialect.class)})
2436
public class SingleSelectionArrayResultTest {
25-
@Test
26-
public void testArrayResult(SessionFactoryScope scope) {
37+
38+
static class TestArguments implements ArgumentsProvider {
39+
@Override
40+
public Stream<? extends Arguments> provideArguments(ExtensionContext extensionContext) {
41+
return Stream.of(
42+
Arguments.of( "select 1", null, null ),
43+
Arguments.of( "select cast(1 as integer)", null, null ),
44+
Arguments.of( "select id from BasicEntity", null, null ),
45+
Arguments.of( "select cast(id as integer) from BasicEntity", null, null ),
46+
Arguments.of( "select ?1", 1, 1 ),
47+
Arguments.of( "select :p1", "p1", 1 ),
48+
Arguments.of( "select cast(?1 as integer)", 1, 1 ),
49+
Arguments.of( "select cast(:p1 as integer)", "p1", 1 )
50+
);
51+
}
52+
}
53+
54+
@ParameterizedTest
55+
@ArgumentsSource(TestArguments.class)
56+
public void testQueryObjectResult(String ql, Object arg1, Object arg2, SessionFactoryScope scope) {
57+
scope.inTransaction( session -> {
58+
Query<Object> query = session.createQuery( ql, Object.class );
59+
if ( arg1 instanceof Integer ) {
60+
query.setParameter( (Integer) arg1, arg2 );
61+
}
62+
if ( arg1 instanceof String ) {
63+
query.setParameter( (String) arg1, arg2 );
64+
}
65+
assertThat( query.getSingleResult() ).isInstanceOf( Integer.class ).isEqualTo( 1 );
66+
} );
67+
}
68+
69+
@ParameterizedTest
70+
@ArgumentsSource(TestArguments.class)
71+
public void testNativeQueryObjectResult(String ql, Object arg1, Object arg2, SessionFactoryScope scope) {
72+
scope.inTransaction( session -> {
73+
NativeQuery<Object> query = session.createNativeQuery( ql, Object.class );
74+
if ( arg1 instanceof Integer ) {
75+
query.setParameter( (Integer) arg1, arg2 );
76+
}
77+
if ( arg1 instanceof String ) {
78+
query.setParameter( (String) arg1, arg2 );
79+
}
80+
assertThat( query.getSingleResult() ).isInstanceOf( Integer.class ).isEqualTo( 1 );
81+
} );
82+
}
83+
84+
@ParameterizedTest
85+
@ArgumentsSource(TestArguments.class)
86+
public void testSelectionQueryObjectResult(String ql, Object arg1, Object arg2, SessionFactoryScope scope) {
87+
scope.inTransaction( session -> {
88+
SelectionQuery<Object> query = session.createSelectionQuery( ql, Object.class );
89+
if ( arg1 instanceof Integer ) {
90+
query.setParameter( (Integer) arg1, arg2 );
91+
}
92+
if ( arg1 instanceof String ) {
93+
query.setParameter( (String) arg1, arg2 );
94+
}
95+
assertThat( query.getSingleResult() ).isInstanceOf( Integer.class ).isEqualTo( 1 );
96+
} );
97+
}
98+
99+
@ParameterizedTest
100+
@ArgumentsSource(TestArguments.class)
101+
public void testQueryArrayResult(String ql, Object arg1, Object arg2, SessionFactoryScope scope) {
102+
scope.inTransaction( session -> {
103+
Query<Object[]> query = session.createQuery( ql, Object[].class );
104+
if ( arg1 instanceof Integer ) {
105+
query.setParameter( (Integer) arg1, arg2 );
106+
}
107+
if ( arg1 instanceof String ) {
108+
query.setParameter( (String) arg1, arg2 );
109+
}
110+
assertThat( query.getSingleResult() ).containsExactly( 1 );
111+
} );
112+
}
113+
114+
@ParameterizedTest
115+
@ArgumentsSource(TestArguments.class)
116+
public void testNativeQueryArrayResult(String ql, Object arg1, Object arg2, SessionFactoryScope scope) {
27117
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 );
118+
NativeQuery<Object[]> query = session.createNativeQuery( ql, Object[].class );
119+
if ( arg1 instanceof Integer ) {
120+
query.setParameter( (Integer) arg1, arg2 );
121+
}
122+
if ( arg1 instanceof String ) {
123+
query.setParameter( (String) arg1, arg2 );
124+
}
125+
assertThat( query.getSingleResult() ).containsExactly( 1 );
52126
} );
53127
}
54128

55-
@Test
56-
public void testNormalResult(SessionFactoryScope scope) {
129+
@ParameterizedTest
130+
@ArgumentsSource(TestArguments.class)
131+
public void testSelectionQueryArrayResult(String ql, Object arg1, Object arg2, SessionFactoryScope scope) {
57132
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 );
133+
SelectionQuery<Object[]> query = session.createSelectionQuery( ql, Object[].class );
134+
if ( arg1 instanceof Integer ) {
135+
query.setParameter( (Integer) arg1, arg2 );
136+
}
137+
if ( arg1 instanceof String ) {
138+
query.setParameter( (String) arg1, arg2 );
139+
}
140+
assertThat( query.getSingleResult() ).containsExactly( 1 );
82141
} );
83142
}
84143

0 commit comments

Comments
 (0)