diff --git a/databases.gradle b/databases.gradle
index 0184ab7b57..f083801fae 100644
--- a/databases.gradle
+++ b/databases.gradle
@@ -37,6 +37,13 @@ ext {
'jdbc.pass' : 'hibernate_orm_test',
'jdbc.url' : 'jdbc:postgresql:hibernate_orm_test'
],
+ pgsql_docker : [
+ 'db.dialect' : 'org.hibernate.dialect.PostgreSQL95Dialect',
+ 'jdbc.driver': 'org.postgresql.Driver',
+ 'jdbc.user' : 'hibernate_orm_test',
+ 'jdbc.pass' : 'hibernate_orm_test',
+ 'jdbc.url' : 'jdbc:postgresql://127.0.0.1/hibernate_orm_test'
+ ],
mysql : [
'db.dialect' : 'org.hibernate.dialect.MySQL57Dialect',
'jdbc.driver': 'com.mysql.jdbc.Driver',
diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/AvailableSettings.java b/hibernate-core/src/main/java/org/hibernate/cfg/AvailableSettings.java
index a8a12c0e68..3ff3d7cd2c 100644
--- a/hibernate-core/src/main/java/org/hibernate/cfg/AvailableSettings.java
+++ b/hibernate-core/src/main/java/org/hibernate/cfg/AvailableSettings.java
@@ -1570,7 +1570,7 @@ public interface AvailableSettings {
* Global setting for whether NULL parameter bindings should be passed to database
* procedure/function calls as part of {@link org.hibernate.procedure.ProcedureCall}
* handling. Implicitly Hibernate will not pass the NULL, the intention being to allow
- * any default argumnet values to be applied.
+ * any default argument values to be applied.
*
* This defines a global setting, which can them be controlled per parameter via
* {@link org.hibernate.procedure.ParameterRegistration#enablePassingNulls(boolean)}
diff --git a/hibernate-core/src/main/java/org/hibernate/procedure/internal/AbstractParameterRegistrationImpl.java b/hibernate-core/src/main/java/org/hibernate/procedure/internal/AbstractParameterRegistrationImpl.java
index 457185f843..31d3c5a257 100644
--- a/hibernate-core/src/main/java/org/hibernate/procedure/internal/AbstractParameterRegistrationImpl.java
+++ b/hibernate-core/src/main/java/org/hibernate/procedure/internal/AbstractParameterRegistrationImpl.java
@@ -8,6 +8,7 @@ package org.hibernate.procedure.internal;
import java.sql.CallableStatement;
import java.sql.SQLException;
+import java.sql.Types;
import java.util.Calendar;
import java.util.Date;
import javax.persistence.ParameterMode;
@@ -117,11 +118,12 @@ public abstract class AbstractParameterRegistrationImpl implements ParameterR
this.type = type;
if ( mode == ParameterMode.REF_CURSOR ) {
- return;
+ this.sqlTypes = new int[]{ Types.REF_CURSOR };
+ }
+ else {
+ this.passNulls = initialPassNullsSetting;
+ setHibernateType( hibernateType );
}
-
- this.passNulls = initialPassNullsSetting;
- setHibernateType( hibernateType );
}
private AbstractParameterRegistrationImpl(
@@ -385,9 +387,6 @@ public abstract class AbstractParameterRegistrationImpl implements ParameterR
if ( mode == ParameterMode.IN ) {
throw new ParameterMisuseException( "IN parameter not valid for output extraction" );
}
- else if ( mode == ParameterMode.REF_CURSOR ) {
- throw new ParameterMisuseException( "REF_CURSOR parameters should be accessed via results" );
- }
// TODO: sqlTypesToUse.length > 1 does not seem to have a working use case (HHH-10769).
// For now, if sqlTypes.length > 1 with a named parameter, then extract
diff --git a/hibernate-core/src/main/java/org/hibernate/procedure/internal/ProcedureOutputsImpl.java b/hibernate-core/src/main/java/org/hibernate/procedure/internal/ProcedureOutputsImpl.java
index 3849161f90..5552e1c66c 100644
--- a/hibernate-core/src/main/java/org/hibernate/procedure/internal/ProcedureOutputsImpl.java
+++ b/hibernate-core/src/main/java/org/hibernate/procedure/internal/ProcedureOutputsImpl.java
@@ -8,6 +8,8 @@ package org.hibernate.procedure.internal;
import java.sql.CallableStatement;
import java.sql.ResultSet;
+import java.util.List;
+import java.util.function.Supplier;
import org.hibernate.engine.jdbc.cursor.spi.RefCursorSupport;
import org.hibernate.procedure.ParameterRegistration;
@@ -90,7 +92,7 @@ public class ProcedureOutputsImpl extends OutputsImpl implements ProcedureOutput
.getService( RefCursorSupport.class )
.getResultSet( ProcedureOutputsImpl.this.callableStatement, refCursorParam.getPosition() );
}
- return buildResultSetOutput( extractResults( resultSet ) );
+ return buildResultSetOutput( () -> extractResults( resultSet ) );
}
}
diff --git a/hibernate-core/src/main/java/org/hibernate/result/internal/OutputsImpl.java b/hibernate-core/src/main/java/org/hibernate/result/internal/OutputsImpl.java
index 1ff43316a4..97a13b2ae4 100644
--- a/hibernate-core/src/main/java/org/hibernate/result/internal/OutputsImpl.java
+++ b/hibernate-core/src/main/java/org/hibernate/result/internal/OutputsImpl.java
@@ -13,6 +13,7 @@ import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.function.Supplier;
import org.hibernate.JDBCException;
import org.hibernate.engine.spi.QueryParameters;
@@ -201,6 +202,10 @@ public class OutputsImpl implements Outputs {
return new ResultSetOutputImpl( list );
}
+ protected Output buildResultSetOutput(Supplier listSupplier) {
+ return new ResultSetOutputImpl( listSupplier );
+ }
+
protected Output buildUpdateCountOutput(int updateCount) {
return new UpdateCountOutputImpl( updateCount );
}
diff --git a/hibernate-core/src/main/java/org/hibernate/result/internal/ResultSetOutputImpl.java b/hibernate-core/src/main/java/org/hibernate/result/internal/ResultSetOutputImpl.java
index fa85ace480..0917833ef6 100644
--- a/hibernate-core/src/main/java/org/hibernate/result/internal/ResultSetOutputImpl.java
+++ b/hibernate-core/src/main/java/org/hibernate/result/internal/ResultSetOutputImpl.java
@@ -6,7 +6,11 @@
*/
package org.hibernate.result.internal;
+import java.sql.ResultSet;
import java.util.List;
+import java.util.function.Supplier;
+
+import javax.enterprise.inject.spi.Producer;
import org.hibernate.result.ResultSetOutput;
@@ -16,10 +20,14 @@ import org.hibernate.result.ResultSetOutput;
* @author Steve Ebersole
*/
class ResultSetOutputImpl implements ResultSetOutput {
- private final List results;
+ private final Supplier resultSetSupplier;
public ResultSetOutputImpl(List results) {
- this.results = results;
+ this.resultSetSupplier = () -> results;
+ }
+
+ public ResultSetOutputImpl(Supplier resultSetSupplier) {
+ this.resultSetSupplier = resultSetSupplier;
}
@Override
@@ -30,7 +38,7 @@ class ResultSetOutputImpl implements ResultSetOutput {
@Override
@SuppressWarnings("unchecked")
public List getResultList() {
- return results;
+ return resultSetSupplier.get();
}
@Override
diff --git a/hibernate-core/src/test/java/org/hibernate/test/procedure/MySQLStoredProcedureTest.java b/hibernate-core/src/test/java/org/hibernate/test/procedure/MySQLStoredProcedureTest.java
index f6a07b8f3a..2500c4d020 100644
--- a/hibernate-core/src/test/java/org/hibernate/test/procedure/MySQLStoredProcedureTest.java
+++ b/hibernate-core/src/test/java/org/hibernate/test/procedure/MySQLStoredProcedureTest.java
@@ -40,8 +40,8 @@ public class MySQLStoredProcedureTest extends BaseEntityManagerFunctionalTestCas
@Override
protected Class>[] getAnnotatedClasses() {
return new Class>[] {
- Person.class,
- Phone.class,
+ Person.class,
+ Phone.class,
};
}
@@ -54,51 +54,50 @@ public class MySQLStoredProcedureTest extends BaseEntityManagerFunctionalTestCas
try {
Session session = entityManager.unwrap( Session.class );
- session.doWork( new Work() {
- @Override
- public void execute(Connection connection) throws SQLException {
- Statement statement = null;
- try {
- statement = connection.createStatement();
- statement.executeUpdate(
- "CREATE PROCEDURE sp_count_phones (" +
- " IN personId INT, " +
- " OUT phoneCount INT " +
- ") " +
- "BEGIN " +
- " SELECT COUNT(*) INTO phoneCount " +
- " FROM Phone p " +
- " WHERE p.person_id = personId; " +
- "END"
- );
- statement.executeUpdate(
- "CREATE PROCEDURE sp_phones(IN personId INT) " +
- "BEGIN " +
- " SELECT * " +
- " FROM Phone " +
- " WHERE person_id = personId; " +
- "END"
- );
- statement.executeUpdate(
- "CREATE FUNCTION fn_count_phones(personId integer) " +
- "RETURNS integer " +
- "DETERMINISTIC " +
- "READS SQL DATA " +
- "BEGIN " +
- " DECLARE phoneCount integer; " +
- " SELECT COUNT(*) INTO phoneCount " +
- " FROM Phone p " +
- " WHERE p.person_id = personId; " +
- " RETURN phoneCount; " +
- "END"
- );
- } finally {
- if ( statement != null ) {
- statement.close();
- }
+ session.doWork( connection -> {
+ Statement statement = null;
+ try {
+ statement = connection.createStatement();
+ statement.executeUpdate(
+ "CREATE PROCEDURE sp_count_phones (" +
+ " IN personId INT, " +
+ " OUT phoneCount INT " +
+ ") " +
+ "BEGIN " +
+ " SELECT COUNT(*) INTO phoneCount " +
+ " FROM Phone p " +
+ " WHERE p.person_id = personId; " +
+ "END"
+ );
+
+ statement.executeUpdate(
+ "CREATE PROCEDURE sp_phones(IN personId INT) " +
+ "BEGIN " +
+ " SELECT * " +
+ " FROM Phone " +
+ " WHERE person_id = personId; " +
+ "END"
+ );
+
+ statement.executeUpdate(
+ "CREATE FUNCTION fn_count_phones(personId integer) " +
+ "RETURNS integer " +
+ "DETERMINISTIC " +
+ "READS SQL DATA " +
+ "BEGIN " +
+ " DECLARE phoneCount integer; " +
+ " SELECT COUNT(*) INTO phoneCount " +
+ " FROM Phone p " +
+ " WHERE p.person_id = personId; " +
+ " RETURN phoneCount; " +
+ "END"
+ );
+ } finally {
+ if ( statement != null ) {
+ statement.close();
}
}
- } );
+ } );
}
finally {
entityManager.getTransaction().rollback();
@@ -140,16 +139,13 @@ public class MySQLStoredProcedureTest extends BaseEntityManagerFunctionalTestCas
try {
Session session = entityManager.unwrap( Session.class );
- session.doWork( new Work() {
- @Override
- public void execute(Connection connection) throws SQLException {
- try (Statement statement = connection.createStatement()) {
- statement.executeUpdate( "DROP PROCEDURE IF EXISTS sp_count_phones" );
- }
- catch (SQLException ignore) {
- }
+ session.doWork( connection -> {
+ try (Statement statement = connection.createStatement()) {
+ statement.executeUpdate( "DROP PROCEDURE IF EXISTS sp_count_phones" );
}
- } );
+ catch (SQLException ignore) {
+ }
+ } );
}
finally {
entityManager.getTransaction().rollback();
@@ -161,16 +157,13 @@ public class MySQLStoredProcedureTest extends BaseEntityManagerFunctionalTestCas
try {
Session session = entityManager.unwrap( Session.class );
- session.doWork( new Work() {
- @Override
- public void execute(Connection connection) throws SQLException {
- try (Statement statement = connection.createStatement()) {
- statement.executeUpdate( "DROP PROCEDURE IF EXISTS sp_phones" );
- }
- catch (SQLException ignore) {
- }
+ session.doWork( connection -> {
+ try (Statement statement = connection.createStatement()) {
+ statement.executeUpdate( "DROP PROCEDURE IF EXISTS sp_phones" );
}
- } );
+ catch (SQLException ignore) {
+ }
+ } );
}
finally {
entityManager.getTransaction().rollback();
@@ -182,16 +175,13 @@ public class MySQLStoredProcedureTest extends BaseEntityManagerFunctionalTestCas
try {
Session session = entityManager.unwrap( Session.class );
- session.doWork( new Work() {
- @Override
- public void execute(Connection connection) throws SQLException {
- try (Statement statement = connection.createStatement()) {
- statement.executeUpdate( "DROP FUNCTION IF EXISTS fn_count_phones" );
- }
- catch (SQLException ignore) {
- }
+ session.doWork( connection -> {
+ try (Statement statement = connection.createStatement()) {
+ statement.executeUpdate( "DROP FUNCTION IF EXISTS fn_count_phones" );
}
- } );
+ catch (SQLException ignore) {
+ }
+ } );
}
finally {
entityManager.getTransaction().rollback();
diff --git a/hibernate-core/src/test/java/org/hibernate/test/procedure/OracleStoredProcedureTest.java b/hibernate-core/src/test/java/org/hibernate/test/procedure/OracleStoredProcedureTest.java
index d4de68669f..b9ce162dee 100644
--- a/hibernate-core/src/test/java/org/hibernate/test/procedure/OracleStoredProcedureTest.java
+++ b/hibernate-core/src/test/java/org/hibernate/test/procedure/OracleStoredProcedureTest.java
@@ -11,8 +11,13 @@ import java.sql.Types;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.List;
+import javax.persistence.Entity;
import javax.persistence.EntityManager;
+import javax.persistence.Id;
+import javax.persistence.NamedStoredProcedureQueries;
+import javax.persistence.NamedStoredProcedureQuery;
import javax.persistence.ParameterMode;
+import javax.persistence.StoredProcedureParameter;
import javax.persistence.StoredProcedureQuery;
import org.hibernate.Session;
@@ -25,12 +30,17 @@ import org.hibernate.result.ResultSetOutput;
import org.hibernate.testing.FailureExpected;
import org.hibernate.testing.RequiresDialect;
+import org.hibernate.testing.TestForIssue;
+import org.hibernate.testing.transaction.TransactionUtil;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
+import static org.hibernate.testing.transaction.TransactionUtil.doInJPA;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
/**
@@ -44,9 +54,34 @@ public class OracleStoredProcedureTest extends BaseEntityManagerFunctionalTestCa
return new Class>[] {
Person.class,
Phone.class,
+ IdHolder.class
};
}
+ @NamedStoredProcedureQueries({
+ @NamedStoredProcedureQuery(
+ name = "singleRefCursor",
+ procedureName = "singleRefCursor",
+ parameters = {
+ @StoredProcedureParameter(mode = ParameterMode.REF_CURSOR, type = void.class)
+ }
+ ),
+ @NamedStoredProcedureQuery(
+ name = "outAndRefCursor",
+ procedureName = "outAndRefCursor",
+ parameters = {
+ @StoredProcedureParameter(mode = ParameterMode.REF_CURSOR, type = void.class),
+ @StoredProcedureParameter(mode = ParameterMode.OUT, type = Long.class),
+ }
+ )
+ })
+ @Entity(name = "IdHolder")
+ public static class IdHolder {
+
+ @Id
+ Long id;
+ }
+
@Before
public void init() {
EntityManager entityManager = createEntityManager();
@@ -55,79 +90,95 @@ public class OracleStoredProcedureTest extends BaseEntityManagerFunctionalTestCa
try {
Session session = entityManager.unwrap( Session.class );
- session.doWork( new Work() {
- @Override
- public void execute(Connection connection) throws SQLException {
- Statement statement = null;
- try {
- statement = connection.createStatement();
- statement.executeUpdate(
- "CREATE OR REPLACE PROCEDURE sp_count_phones ( " +
- " personId IN NUMBER, " +
- " phoneCount OUT NUMBER ) " +
- "AS " +
- "BEGIN " +
- " SELECT COUNT(*) INTO phoneCount " +
- " FROM phone " +
- " WHERE person_id = personId; " +
- "END;"
- );
- statement.executeUpdate(
- "CREATE OR REPLACE PROCEDURE sp_person_phones ( " +
- " personId IN NUMBER, " +
- " personPhones OUT SYS_REFCURSOR ) " +
- "AS " +
- "BEGIN " +
- " OPEN personPhones FOR " +
- " SELECT *" +
- " FROM phone " +
- " WHERE person_id = personId; " +
- "END;"
- );
- statement.executeUpdate(
- "CREATE OR REPLACE FUNCTION fn_count_phones ( " +
- " personId IN NUMBER ) " +
- " RETURN NUMBER " +
- "IS " +
- " phoneCount NUMBER; " +
- "BEGIN " +
- " SELECT COUNT(*) INTO phoneCount " +
- " FROM phone " +
- " WHERE person_id = personId; " +
- " RETURN( phoneCount ); " +
- "END;"
- );
- statement.executeUpdate(
- "CREATE OR REPLACE FUNCTION fn_person_and_phones ( " +
- " personId IN NUMBER ) " +
- " RETURN SYS_REFCURSOR " +
- "IS " +
- " personAndPhones SYS_REFCURSOR; " +
- "BEGIN " +
- " OPEN personAndPhones FOR " +
- " SELECT " +
- " pr.id AS \"pr.id\", " +
- " pr.name AS \"pr.name\", " +
- " pr.nickName AS \"pr.nickName\", " +
- " pr.address AS \"pr.address\", " +
- " pr.createdOn AS \"pr.createdOn\", " +
- " pr.version AS \"pr.version\", " +
- " ph.id AS \"ph.id\", " +
- " ph.person_id AS \"ph.person_id\", " +
- " ph.phone_number AS \"ph.phone_number\" " +
- " FROM person pr " +
- " JOIN phone ph ON pr.id = ph.person_id " +
- " WHERE pr.id = personId; " +
- " RETURN personAndPhones; " +
- "END;"
- );
- } finally {
- if ( statement != null ) {
- statement.close();
- }
- }
- }
- } );
+ session.doWork( connection -> {
+ Statement statement = null;
+ try {
+ statement = connection.createStatement();
+ statement.executeUpdate(
+ "CREATE OR REPLACE PROCEDURE sp_count_phones ( " +
+ " personId IN NUMBER, " +
+ " phoneCount OUT NUMBER ) " +
+ "AS " +
+ "BEGIN " +
+ " SELECT COUNT(*) INTO phoneCount " +
+ " FROM phone " +
+ " WHERE person_id = personId; " +
+ "END;"
+ );
+ statement.executeUpdate(
+ "CREATE OR REPLACE PROCEDURE sp_person_phones ( " +
+ " personId IN NUMBER, " +
+ " personPhones OUT SYS_REFCURSOR ) " +
+ "AS " +
+ "BEGIN " +
+ " OPEN personPhones FOR " +
+ " SELECT *" +
+ " FROM phone " +
+ " WHERE person_id = personId; " +
+ "END;"
+ );
+ statement.executeUpdate(
+ "CREATE OR REPLACE FUNCTION fn_count_phones ( " +
+ " personId IN NUMBER ) " +
+ " RETURN NUMBER " +
+ "IS " +
+ " phoneCount NUMBER; " +
+ "BEGIN " +
+ " SELECT COUNT(*) INTO phoneCount " +
+ " FROM phone " +
+ " WHERE person_id = personId; " +
+ " RETURN( phoneCount ); " +
+ "END;"
+ );
+ statement.executeUpdate(
+ "CREATE OR REPLACE FUNCTION fn_person_and_phones ( " +
+ " personId IN NUMBER ) " +
+ " RETURN SYS_REFCURSOR " +
+ "IS " +
+ " personAndPhones SYS_REFCURSOR; " +
+ "BEGIN " +
+ " OPEN personAndPhones FOR " +
+ " SELECT " +
+ " pr.id AS \"pr.id\", " +
+ " pr.name AS \"pr.name\", " +
+ " pr.nickName AS \"pr.nickName\", " +
+ " pr.address AS \"pr.address\", " +
+ " pr.createdOn AS \"pr.createdOn\", " +
+ " pr.version AS \"pr.version\", " +
+ " ph.id AS \"ph.id\", " +
+ " ph.person_id AS \"ph.person_id\", " +
+ " ph.phone_number AS \"ph.phone_number\" " +
+ " FROM person pr " +
+ " JOIN phone ph ON pr.id = ph.person_id " +
+ " WHERE pr.id = personId; " +
+ " RETURN personAndPhones; " +
+ "END;"
+ );
+ statement.executeUpdate(
+ "CREATE OR REPLACE " +
+ "PROCEDURE singleRefCursor(p_recordset OUT SYS_REFCURSOR) AS " +
+ " BEGIN " +
+ " OPEN p_recordset FOR " +
+ " SELECT 1 as id " +
+ " FROM dual; " +
+ " END; "
+ );
+ statement.executeUpdate(
+ "CREATE OR REPLACE " +
+ "PROCEDURE outAndRefCursor(p_recordset OUT SYS_REFCURSOR, p_value OUT NUMBER) AS " +
+ " BEGIN " +
+ " OPEN p_recordset FOR " +
+ " SELECT 1 as id " +
+ " FROM dual; " +
+ " SELECT 1 INTO p_value FROM dual; " +
+ " END; "
+ );
+ } finally {
+ if ( statement != null ) {
+ statement.close();
+ }
+ }
+ } );
}
finally {
entityManager.getTransaction().rollback();
@@ -357,4 +408,54 @@ public class OracleStoredProcedureTest extends BaseEntityManagerFunctionalTestCa
entityManager.close();
}
}
+
+ @Test
+ @TestForIssue( jiraKey = "HHH-11863")
+ public void testSysRefCursorAsOutParameter() {
+
+ doInJPA( this::entityManagerFactory, entityManager -> {
+ StoredProcedureQuery function = entityManager.createNamedStoredProcedureQuery("singleRefCursor");
+
+ function.execute();
+
+ assertFalse( function.hasMoreResults() );
+ Long value = null;
+
+ try ( ResultSet resultSet = (ResultSet) function.getOutputParameterValue( 1 ) ) {
+ while ( resultSet.next() ) {
+ value = resultSet.getLong( 1 );
+ }
+ }
+ catch (SQLException e) {
+ fail(e.getMessage());
+ }
+
+ assertEquals( Long.valueOf( 1 ), value );
+ } );
+ }
+
+ @Test
+ @TestForIssue( jiraKey = "HHH-11863")
+ public void testOutAndSysRefCursorAsOutParameter() {
+
+ doInJPA( this::entityManagerFactory, entityManager -> {
+ StoredProcedureQuery function = entityManager.createNamedStoredProcedureQuery("outAndRefCursor");
+
+ function.execute();
+
+ assertFalse( function.hasMoreResults() );
+ Long value = null;
+
+ try ( ResultSet resultSet = (ResultSet) function.getOutputParameterValue( 1 ) ) {
+ while ( resultSet.next() ) {
+ value = resultSet.getLong( 1 );
+ }
+ }
+ catch (SQLException e) {
+ fail(e.getMessage());
+ }
+
+ assertEquals( value, function.getOutputParameterValue( 2 ) );
+ } );
+ }
}
diff --git a/hibernate-core/src/test/java/org/hibernate/test/procedure/PostgreSQLStoredProcedureTest.java b/hibernate-core/src/test/java/org/hibernate/test/procedure/PostgreSQLStoredProcedureTest.java
index 662cad9623..e256c1afa4 100644
--- a/hibernate-core/src/test/java/org/hibernate/test/procedure/PostgreSQLStoredProcedureTest.java
+++ b/hibernate-core/src/test/java/org/hibernate/test/procedure/PostgreSQLStoredProcedureTest.java
@@ -1,7 +1,7 @@
package org.hibernate.test.procedure;
import java.sql.CallableStatement;
-import java.sql.Connection;
+import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.sql.Statement;
@@ -10,21 +10,22 @@ import java.sql.Types;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.List;
-import javax.persistence.EntityManager;
import javax.persistence.ParameterMode;
import javax.persistence.StoredProcedureQuery;
import org.hibernate.Session;
import org.hibernate.dialect.PostgreSQL81Dialect;
-import org.hibernate.jdbc.ReturningWork;
-import org.hibernate.jdbc.Work;
import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase;
import org.hibernate.testing.RequiresDialect;
+import org.hibernate.testing.TestForIssue;
import org.junit.Before;
import org.junit.Test;
+import static org.hibernate.testing.transaction.TransactionUtil.doInJPA;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.fail;
/**
* @author Vlad Mihalcea
@@ -36,19 +37,16 @@ public class PostgreSQLStoredProcedureTest extends BaseEntityManagerFunctionalTe
protected Class>[] getAnnotatedClasses() {
return new Class>[] {
Person.class,
- Phone.class,
+ Phone.class
};
}
@Before
public void init() {
- EntityManager entityManager = createEntityManager();
- entityManager.getTransaction().begin();
- Session session = entityManager.unwrap( Session.class );
+ doInJPA( this::entityManagerFactory, entityManager -> {
+ Session session = entityManager.unwrap( Session.class );
- session.doWork( new Work() {
- @Override
- public void execute(Connection connection) throws SQLException {
+ session.doWork( connection -> {
Statement statement = null;
try {
statement = connection.createStatement();
@@ -61,19 +59,13 @@ public class PostgreSQLStoredProcedureTest extends BaseEntityManagerFunctionalTe
statement.close();
}
}
- }
+ } );
} );
- entityManager.getTransaction().commit();
- entityManager.close();
+ doInJPA( this::entityManagerFactory, entityManager -> {
+ Session session = entityManager.unwrap( Session.class );
- entityManager = createEntityManager();
- entityManager.getTransaction().begin();
- session = entityManager.unwrap( Session.class );
-
- session.doWork( new Work() {
- @Override
- public void execute(Connection connection) throws SQLException {
+ session.doWork( connection -> {
Statement statement = null;
try {
statement = connection.createStatement();
@@ -86,19 +78,32 @@ public class PostgreSQLStoredProcedureTest extends BaseEntityManagerFunctionalTe
statement.close();
}
}
- }
+ } );
} );
- entityManager.getTransaction().commit();
- entityManager.close();
+ doInJPA( this::entityManagerFactory, entityManager -> {
+ Session session = entityManager.unwrap( Session.class );
- entityManager = createEntityManager();
- entityManager.getTransaction().begin();
- session = entityManager.unwrap( Session.class );
+ session.doWork( connection -> {
+ Statement statement = null;
+ try {
+ statement = connection.createStatement();
+ statement.executeUpdate( "DROP FUNCTION singleRefCursor(bigint)" );
+ }
+ catch (SQLException ignore) {
+ }
+ finally {
+ if ( statement != null ) {
+ statement.close();
+ }
+ }
+ } );
+ } );
- session.doWork( new Work() {
- @Override
- public void execute(Connection connection) throws SQLException {
+ doInJPA( this::entityManagerFactory, entityManager -> {
+ Session session = entityManager.unwrap( Session.class );
+
+ session.doWork( connection -> {
Statement statement = null;
try {
statement = connection.createStatement();
@@ -133,48 +138,52 @@ public class PostgreSQLStoredProcedureTest extends BaseEntityManagerFunctionalTe
"$BODY$ " +
"LANGUAGE plpgsql"
);
+
+ statement.executeUpdate(
+ "CREATE OR REPLACE FUNCTION singleRefCursor() " +
+ " RETURNS REFCURSOR AS " +
+ "$BODY$ " +
+ " DECLARE " +
+ " p_recordset REFCURSOR; " +
+ " BEGIN " +
+ " OPEN p_recordset FOR SELECT 1; " +
+ " RETURN p_recordset; " +
+ " END; " +
+ "$BODY$ " +
+ "LANGUAGE plpgsql;"
+ );
}
finally {
if ( statement != null ) {
statement.close();
}
}
- }
+ } );
} );
- entityManager.getTransaction().commit();
- entityManager.close();
+ doInJPA( this::entityManagerFactory, entityManager -> {
+ Person person1 = new Person( "John Doe" );
+ person1.setNickName( "JD" );
+ person1.setAddress( "Earth" );
+ person1.setCreatedOn( Timestamp.from( LocalDateTime.of( 2000, 1, 1, 0, 0, 0 ).toInstant( ZoneOffset.UTC ) ) );
- entityManager = createEntityManager();
- entityManager.getTransaction().begin();
+ entityManager.persist( person1 );
- Person person1 = new Person( "John Doe" );
- person1.setNickName( "JD" );
- person1.setAddress( "Earth" );
- person1.setCreatedOn( Timestamp.from( LocalDateTime.of( 2000, 1, 1, 0, 0, 0 ).toInstant( ZoneOffset.UTC ) ) );
+ Phone phone1 = new Phone( "123-456-7890" );
+ phone1.setId( 1L );
- entityManager.persist( person1 );
+ person1.addPhone( phone1 );
- Phone phone1 = new Phone( "123-456-7890" );
- phone1.setId( 1L );
+ Phone phone2 = new Phone( "098_765-4321" );
+ phone2.setId( 2L );
- person1.addPhone( phone1 );
-
- Phone phone2 = new Phone( "098_765-4321" );
- phone2.setId( 2L );
-
- person1.addPhone( phone2 );
-
- entityManager.getTransaction().commit();
- entityManager.close();
- }
+ person1.addPhone( phone2 );
+ } );
+ }
@Test
public void testStoredProcedureOutParameter() {
- EntityManager entityManager = createEntityManager();
- entityManager.getTransaction().begin();
-
- try {
+ doInJPA( this::entityManagerFactory, entityManager -> {
StoredProcedureQuery query = entityManager.createStoredProcedureQuery( "sp_count_phones" );
query.registerStoredProcedureParameter( "personId", Long.class, ParameterMode.IN );
query.registerStoredProcedureParameter( "phoneCount", Long.class, ParameterMode.OUT );
@@ -184,19 +193,12 @@ public class PostgreSQLStoredProcedureTest extends BaseEntityManagerFunctionalTe
query.execute();
Long phoneCount = (Long) query.getOutputParameterValue( "phoneCount" );
assertEquals( Long.valueOf( 2 ), phoneCount );
- }
- finally {
- entityManager.getTransaction().rollback();
- entityManager.close();
- }
+ } );
}
@Test
public void testStoredProcedureRefCursor() {
- EntityManager entityManager = createEntityManager();
- entityManager.getTransaction().begin();
-
- try {
+ doInJPA( this::entityManagerFactory, entityManager -> {
StoredProcedureQuery query = entityManager.createStoredProcedureQuery( "fn_phones" );
query.registerStoredProcedureParameter( 1, void.class, ParameterMode.REF_CURSOR );
query.registerStoredProcedureParameter( 2, Long.class, ParameterMode.IN );
@@ -205,56 +207,38 @@ public class PostgreSQLStoredProcedureTest extends BaseEntityManagerFunctionalTe
List