Skip to content

Commit 442f5e6

Browse files
committed
HHH-11863 - Implement REF_CURSOR support for StoredProcedureQuery.getOutputParameterValue(4);
1 parent 287221e commit 442f5e6

File tree

9 files changed

+414
-268
lines changed

9 files changed

+414
-268
lines changed

databases.gradle

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,13 @@ ext {
3737
'jdbc.pass' : 'hibernate_orm_test',
3838
'jdbc.url' : 'jdbc:postgresql:hibernate_orm_test'
3939
],
40+
pgsql_docker : [
41+
'db.dialect' : 'org.hibernate.dialect.PostgreSQL95Dialect',
42+
'jdbc.driver': 'org.postgresql.Driver',
43+
'jdbc.user' : 'hibernate_orm_test',
44+
'jdbc.pass' : 'hibernate_orm_test',
45+
'jdbc.url' : 'jdbc:postgresql://127.0.0.1/hibernate_orm_test'
46+
],
4047
mysql : [
4148
'db.dialect' : 'org.hibernate.dialect.MySQL57Dialect',
4249
'jdbc.driver': 'com.mysql.jdbc.Driver',

hibernate-core/src/main/java/org/hibernate/cfg/AvailableSettings.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1570,7 +1570,7 @@ public interface AvailableSettings {
15701570
* Global setting for whether NULL parameter bindings should be passed to database
15711571
* procedure/function calls as part of {@link org.hibernate.procedure.ProcedureCall}
15721572
* handling. Implicitly Hibernate will not pass the NULL, the intention being to allow
1573-
* any default argumnet values to be applied.
1573+
* any default argument values to be applied.
15741574
* <p/>
15751575
* This defines a global setting, which can them be controlled per parameter via
15761576
* {@link org.hibernate.procedure.ParameterRegistration#enablePassingNulls(boolean)}

hibernate-core/src/main/java/org/hibernate/procedure/internal/AbstractParameterRegistrationImpl.java

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
import java.sql.CallableStatement;
1010
import java.sql.SQLException;
11+
import java.sql.Types;
1112
import java.util.Calendar;
1213
import java.util.Date;
1314
import javax.persistence.ParameterMode;
@@ -117,11 +118,12 @@ private AbstractParameterRegistrationImpl(
117118
this.type = type;
118119

119120
if ( mode == ParameterMode.REF_CURSOR ) {
120-
return;
121+
this.sqlTypes = new int[]{ Types.REF_CURSOR };
122+
}
123+
else {
124+
this.passNulls = initialPassNullsSetting;
125+
setHibernateType( hibernateType );
121126
}
122-
123-
this.passNulls = initialPassNullsSetting;
124-
setHibernateType( hibernateType );
125127
}
126128

127129
private AbstractParameterRegistrationImpl(
@@ -385,9 +387,6 @@ public T extract(CallableStatement statement) {
385387
if ( mode == ParameterMode.IN ) {
386388
throw new ParameterMisuseException( "IN parameter not valid for output extraction" );
387389
}
388-
else if ( mode == ParameterMode.REF_CURSOR ) {
389-
throw new ParameterMisuseException( "REF_CURSOR parameters should be accessed via results" );
390-
}
391390

392391
// TODO: sqlTypesToUse.length > 1 does not seem to have a working use case (HHH-10769).
393392
// For now, if sqlTypes.length > 1 with a named parameter, then extract

hibernate-core/src/main/java/org/hibernate/procedure/internal/ProcedureOutputsImpl.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88

99
import java.sql.CallableStatement;
1010
import java.sql.ResultSet;
11+
import java.util.List;
12+
import java.util.function.Supplier;
1113

1214
import org.hibernate.engine.jdbc.cursor.spi.RefCursorSupport;
1315
import org.hibernate.procedure.ParameterRegistration;
@@ -90,7 +92,7 @@ protected Output buildExtendedReturn() {
9092
.getService( RefCursorSupport.class )
9193
.getResultSet( ProcedureOutputsImpl.this.callableStatement, refCursorParam.getPosition() );
9294
}
93-
return buildResultSetOutput( extractResults( resultSet ) );
95+
return buildResultSetOutput( () -> extractResults( resultSet ) );
9496
}
9597
}
9698

hibernate-core/src/main/java/org/hibernate/result/internal/OutputsImpl.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import java.util.List;
1414
import java.util.Map;
1515
import java.util.Set;
16+
import java.util.function.Supplier;
1617

1718
import org.hibernate.JDBCException;
1819
import org.hibernate.engine.spi.QueryParameters;
@@ -201,6 +202,10 @@ protected Output buildResultSetOutput(List list) {
201202
return new ResultSetOutputImpl( list );
202203
}
203204

205+
protected Output buildResultSetOutput(Supplier<List> listSupplier) {
206+
return new ResultSetOutputImpl( listSupplier );
207+
}
208+
204209
protected Output buildUpdateCountOutput(int updateCount) {
205210
return new UpdateCountOutputImpl( updateCount );
206211
}

hibernate-core/src/main/java/org/hibernate/result/internal/ResultSetOutputImpl.java

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,11 @@
66
*/
77
package org.hibernate.result.internal;
88

9+
import java.sql.ResultSet;
910
import java.util.List;
11+
import java.util.function.Supplier;
12+
13+
import javax.enterprise.inject.spi.Producer;
1014

1115
import org.hibernate.result.ResultSetOutput;
1216

@@ -16,10 +20,14 @@
1620
* @author Steve Ebersole
1721
*/
1822
class ResultSetOutputImpl implements ResultSetOutput {
19-
private final List results;
23+
private final Supplier<List> resultSetSupplier;
2024

2125
public ResultSetOutputImpl(List results) {
22-
this.results = results;
26+
this.resultSetSupplier = () -> results;
27+
}
28+
29+
public ResultSetOutputImpl(Supplier<List> resultSetSupplier) {
30+
this.resultSetSupplier = resultSetSupplier;
2331
}
2432

2533
@Override
@@ -30,7 +38,7 @@ public boolean isResultSet() {
3038
@Override
3139
@SuppressWarnings("unchecked")
3240
public List getResultList() {
33-
return results;
41+
return resultSetSupplier.get();
3442
}
3543

3644
@Override

hibernate-core/src/test/java/org/hibernate/test/procedure/MySQLStoredProcedureTest.java

Lines changed: 62 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,8 @@ public class MySQLStoredProcedureTest extends BaseEntityManagerFunctionalTestCas
4040
@Override
4141
protected Class<?>[] getAnnotatedClasses() {
4242
return new Class<?>[] {
43-
Person.class,
44-
Phone.class,
43+
Person.class,
44+
Phone.class,
4545
};
4646
}
4747

@@ -54,51 +54,50 @@ public void init() {
5454

5555
try {
5656
Session session = entityManager.unwrap( Session.class );
57-
session.doWork( new Work() {
58-
@Override
59-
public void execute(Connection connection) throws SQLException {
60-
Statement statement = null;
61-
try {
62-
statement = connection.createStatement();
63-
statement.executeUpdate(
64-
"CREATE PROCEDURE sp_count_phones (" +
65-
" IN personId INT, " +
66-
" OUT phoneCount INT " +
67-
") " +
68-
"BEGIN " +
69-
" SELECT COUNT(*) INTO phoneCount " +
70-
" FROM Phone p " +
71-
" WHERE p.person_id = personId; " +
72-
"END"
73-
);
74-
statement.executeUpdate(
75-
"CREATE PROCEDURE sp_phones(IN personId INT) " +
76-
"BEGIN " +
77-
" SELECT * " +
78-
" FROM Phone " +
79-
" WHERE person_id = personId; " +
80-
"END"
81-
);
82-
statement.executeUpdate(
83-
"CREATE FUNCTION fn_count_phones(personId integer) " +
84-
"RETURNS integer " +
85-
"DETERMINISTIC " +
86-
"READS SQL DATA " +
87-
"BEGIN " +
88-
" DECLARE phoneCount integer; " +
89-
" SELECT COUNT(*) INTO phoneCount " +
90-
" FROM Phone p " +
91-
" WHERE p.person_id = personId; " +
92-
" RETURN phoneCount; " +
93-
"END"
94-
);
95-
} finally {
96-
if ( statement != null ) {
97-
statement.close();
98-
}
57+
session.doWork( connection -> {
58+
Statement statement = null;
59+
try {
60+
statement = connection.createStatement();
61+
statement.executeUpdate(
62+
"CREATE PROCEDURE sp_count_phones (" +
63+
" IN personId INT, " +
64+
" OUT phoneCount INT " +
65+
") " +
66+
"BEGIN " +
67+
" SELECT COUNT(*) INTO phoneCount " +
68+
" FROM Phone p " +
69+
" WHERE p.person_id = personId; " +
70+
"END"
71+
);
72+
73+
statement.executeUpdate(
74+
"CREATE PROCEDURE sp_phones(IN personId INT) " +
75+
"BEGIN " +
76+
" SELECT * " +
77+
" FROM Phone " +
78+
" WHERE person_id = personId; " +
79+
"END"
80+
);
81+
82+
statement.executeUpdate(
83+
"CREATE FUNCTION fn_count_phones(personId integer) " +
84+
"RETURNS integer " +
85+
"DETERMINISTIC " +
86+
"READS SQL DATA " +
87+
"BEGIN " +
88+
" DECLARE phoneCount integer; " +
89+
" SELECT COUNT(*) INTO phoneCount " +
90+
" FROM Phone p " +
91+
" WHERE p.person_id = personId; " +
92+
" RETURN phoneCount; " +
93+
"END"
94+
);
95+
} finally {
96+
if ( statement != null ) {
97+
statement.close();
9998
}
10099
}
101-
} );
100+
} );
102101
}
103102
finally {
104103
entityManager.getTransaction().rollback();
@@ -140,16 +139,13 @@ public void destroy() {
140139

141140
try {
142141
Session session = entityManager.unwrap( Session.class );
143-
session.doWork( new Work() {
144-
@Override
145-
public void execute(Connection connection) throws SQLException {
146-
try (Statement statement = connection.createStatement()) {
147-
statement.executeUpdate( "DROP PROCEDURE IF EXISTS sp_count_phones" );
148-
}
149-
catch (SQLException ignore) {
150-
}
142+
session.doWork( connection -> {
143+
try (Statement statement = connection.createStatement()) {
144+
statement.executeUpdate( "DROP PROCEDURE IF EXISTS sp_count_phones" );
151145
}
152-
} );
146+
catch (SQLException ignore) {
147+
}
148+
} );
153149
}
154150
finally {
155151
entityManager.getTransaction().rollback();
@@ -161,16 +157,13 @@ public void execute(Connection connection) throws SQLException {
161157

162158
try {
163159
Session session = entityManager.unwrap( Session.class );
164-
session.doWork( new Work() {
165-
@Override
166-
public void execute(Connection connection) throws SQLException {
167-
try (Statement statement = connection.createStatement()) {
168-
statement.executeUpdate( "DROP PROCEDURE IF EXISTS sp_phones" );
169-
}
170-
catch (SQLException ignore) {
171-
}
160+
session.doWork( connection -> {
161+
try (Statement statement = connection.createStatement()) {
162+
statement.executeUpdate( "DROP PROCEDURE IF EXISTS sp_phones" );
163+
}
164+
catch (SQLException ignore) {
172165
}
173-
} );
166+
} );
174167
}
175168
finally {
176169
entityManager.getTransaction().rollback();
@@ -182,16 +175,13 @@ public void execute(Connection connection) throws SQLException {
182175

183176
try {
184177
Session session = entityManager.unwrap( Session.class );
185-
session.doWork( new Work() {
186-
@Override
187-
public void execute(Connection connection) throws SQLException {
188-
try (Statement statement = connection.createStatement()) {
189-
statement.executeUpdate( "DROP FUNCTION IF EXISTS fn_count_phones" );
190-
}
191-
catch (SQLException ignore) {
192-
}
178+
session.doWork( connection -> {
179+
try (Statement statement = connection.createStatement()) {
180+
statement.executeUpdate( "DROP FUNCTION IF EXISTS fn_count_phones" );
193181
}
194-
} );
182+
catch (SQLException ignore) {
183+
}
184+
} );
195185
}
196186
finally {
197187
entityManager.getTransaction().rollback();

0 commit comments

Comments
 (0)