HHH-11863 - Implement REF_CURSOR support for StoredProcedureQuery.getOutputParameterValue(4);
This commit is contained in:
parent
287221e26e
commit
442f5e60dd
|
@ -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',
|
||||
|
|
|
@ -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.
|
||||
* <p/>
|
||||
* This defines a global setting, which can them be controlled per parameter via
|
||||
* {@link org.hibernate.procedure.ParameterRegistration#enablePassingNulls(boolean)}
|
||||
|
|
|
@ -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<T> 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<T> 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
|
||||
|
|
|
@ -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 ) );
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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<List> listSupplier) {
|
||||
return new ResultSetOutputImpl( listSupplier );
|
||||
}
|
||||
|
||||
protected Output buildUpdateCountOutput(int updateCount) {
|
||||
return new UpdateCountOutputImpl( updateCount );
|
||||
}
|
||||
|
|
|
@ -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<List> resultSetSupplier;
|
||||
|
||||
public ResultSetOutputImpl(List results) {
|
||||
this.results = results;
|
||||
this.resultSetSupplier = () -> results;
|
||||
}
|
||||
|
||||
public ResultSetOutputImpl(Supplier<List> 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
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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 ) );
|
||||
} );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<Object[]> phones = query.getResultList();
|
||||
assertEquals( 2, phones.size() );
|
||||
}
|
||||
finally {
|
||||
entityManager.getTransaction().rollback();
|
||||
entityManager.close();
|
||||
}
|
||||
} );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFunctionWithJDBC() {
|
||||
EntityManager entityManager = createEntityManager();
|
||||
entityManager.getTransaction().begin();
|
||||
|
||||
try {
|
||||
doInJPA( this::entityManagerFactory, entityManager -> {
|
||||
Session session = entityManager.unwrap( Session.class );
|
||||
Long phoneCount = session.doReturningWork( new ReturningWork<Long>() {
|
||||
@Override
|
||||
public Long execute(Connection connection) throws SQLException {
|
||||
CallableStatement function = null;
|
||||
try {
|
||||
function = connection.prepareCall( "{ ? = call sp_count_phones(?) }" );
|
||||
function.registerOutParameter( 1, Types.BIGINT );
|
||||
function.setLong( 2, 1L );
|
||||
function.execute();
|
||||
return function.getLong( 1 );
|
||||
}
|
||||
finally {
|
||||
if ( function != null ) {
|
||||
function.close();
|
||||
}
|
||||
Long phoneCount = session.doReturningWork( connection -> {
|
||||
CallableStatement function = null;
|
||||
try {
|
||||
function = connection.prepareCall( "{ ? = call sp_count_phones(?) }" );
|
||||
function.registerOutParameter( 1, Types.BIGINT );
|
||||
function.setLong( 2, 1L );
|
||||
function.execute();
|
||||
return function.getLong( 1 );
|
||||
}
|
||||
finally {
|
||||
if ( function != null ) {
|
||||
function.close();
|
||||
}
|
||||
}
|
||||
} );
|
||||
assertEquals( Long.valueOf( 2 ), phoneCount );
|
||||
}
|
||||
finally {
|
||||
entityManager.getTransaction().rollback();
|
||||
entityManager.close();
|
||||
}
|
||||
} );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFunctionWithJDBCByName() {
|
||||
EntityManager entityManager = createEntityManager();
|
||||
entityManager.getTransaction().begin();
|
||||
|
||||
try {
|
||||
Session session = entityManager.unwrap( Session.class );
|
||||
Long phoneCount = session.doReturningWork( new ReturningWork<Long>() {
|
||||
@Override
|
||||
public Long execute(Connection connection) throws SQLException {
|
||||
doInJPA( this::entityManagerFactory, entityManager -> {
|
||||
try {
|
||||
Session session = entityManager.unwrap( Session.class );
|
||||
Long phoneCount = session.doReturningWork( connection -> {
|
||||
CallableStatement function = null;
|
||||
try {
|
||||
function = connection.prepareCall( "{ ? = call sp_count_phones(?) }" );
|
||||
|
@ -268,15 +252,65 @@ public class PostgreSQLStoredProcedureTest extends BaseEntityManagerFunctionalTe
|
|||
function.close();
|
||||
}
|
||||
}
|
||||
} );
|
||||
assertEquals( Long.valueOf( 2 ), phoneCount );
|
||||
} catch (Exception e) {
|
||||
assertEquals( SQLFeatureNotSupportedException.class, e.getCause().getClass() );
|
||||
}
|
||||
} );
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestForIssue( jiraKey = "HHH-11863")
|
||||
public void testSysRefCursorAsOutParameter() {
|
||||
|
||||
doInJPA( this::entityManagerFactory, entityManager -> {
|
||||
Long value = null;
|
||||
|
||||
Session session = entityManager.unwrap( Session.class );
|
||||
|
||||
try(ResultSet resultSet = session.doReturningWork( connection -> {
|
||||
CallableStatement function = null;
|
||||
try {
|
||||
function = connection.prepareCall( "{ ? = call singleRefCursor() }" );
|
||||
function.registerOutParameter( 1, Types.REF_CURSOR );
|
||||
function.execute();
|
||||
return (ResultSet) function.getObject( 1 );
|
||||
}
|
||||
} );
|
||||
assertEquals( Long.valueOf( 2 ), phoneCount );
|
||||
} catch (Exception e) {
|
||||
assertEquals( SQLFeatureNotSupportedException.class, e.getCause().getClass() );
|
||||
}
|
||||
finally {
|
||||
entityManager.getTransaction().rollback();
|
||||
entityManager.close();
|
||||
}
|
||||
finally {
|
||||
if ( function != null ) {
|
||||
function.close();
|
||||
}
|
||||
}
|
||||
} )) {
|
||||
while ( resultSet.next() ) {
|
||||
value = resultSet.getLong( 1 );
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
fail(e.getMessage());
|
||||
}
|
||||
assertEquals( Long.valueOf( 1 ), value );
|
||||
|
||||
|
||||
StoredProcedureQuery function = entityManager.createStoredProcedureQuery( "singleRefCursor" );
|
||||
function.registerStoredProcedureParameter( 1, void.class, ParameterMode.REF_CURSOR );
|
||||
|
||||
function.execute();
|
||||
|
||||
assertFalse( function.hasMoreResults() );
|
||||
|
||||
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 );
|
||||
} );
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue