From e2db993578ab4b803b67e2b27bb74ea2dab346a8 Mon Sep 17 00:00:00 2001 From: Steve Ebersole Date: Wed, 19 Jan 2022 08:04:46 -0600 Subject: [PATCH] clean up remaining test suite failures - stored procedure parameters --- .../procedure/OracleStoredProcedureTest.java | 483 +++++++++--------- .../procedure/ProcedureParameterTests.java | 103 ++++ ...toredProcedureNullParameterByNameTest.java | 174 ------- 3 files changed, 348 insertions(+), 412 deletions(-) create mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/procedure/ProcedureParameterTests.java delete mode 100644 hibernate-core/src/test/java/org/hibernate/test/jpa/procedure/StoredProcedureNullParameterByNameTest.java diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/procedure/OracleStoredProcedureTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/procedure/OracleStoredProcedureTest.java index dae4f358cd..ce70dd5651 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/procedure/OracleStoredProcedureTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/procedure/OracleStoredProcedureTest.java @@ -18,6 +18,7 @@ import java.time.ZoneOffset; import java.util.List; import org.hibernate.Session; +import org.hibernate.annotations.QueryHints; import org.hibernate.dialect.OracleDialect; import org.hibernate.procedure.ProcedureCall; import org.hibernate.query.procedure.ProcedureParameter; @@ -45,6 +46,7 @@ import jakarta.persistence.NamedStoredProcedureQuery; import jakarta.persistence.ParameterMode; import jakarta.persistence.StoredProcedureParameter; import jakarta.persistence.StoredProcedureQuery; +import org.assertj.core.api.Assertions; import static org.hamcrest.CoreMatchers.instanceOf; import static org.hamcrest.MatcherAssert.assertThat; @@ -68,247 +70,25 @@ import static org.junit.jupiter.api.Assertions.fail; @RequiresDialect(value = OracleDialect.class) public class OracleStoredProcedureTest { - @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 { + @Test + public void testUnRegisteredParameter(EntityManagerFactoryScope scope) { + scope.inTransaction( (em) -> { + final StoredProcedureQuery function = em.createStoredProcedureQuery( "find_char", Integer.class ); + function.setHint( QueryHints.CALLABLE_FUNCTION, "true" ); + // search-string + function.registerStoredProcedureParameter( 1, String.class, ParameterMode.IN ); + // source-string + function.registerStoredProcedureParameter( 2, String.class, ParameterMode.IN ); - @Id - Long id; + function.setParameter( 1, "." ); + function.setParameter( 2, "org.hibernate.query" ); + + final Object singleResult = function.getSingleResult(); + Assertions.assertThat( singleResult ).isInstanceOf( Integer.class ); + Assertions.assertThat( singleResult ).isEqualTo( 4 ); + } ); } - @BeforeEach - public void init(EntityManagerFactoryScope scope) { - EntityManager entityManager = scope.getEntityManagerFactory().createEntityManager(); - entityManager.getTransaction().begin(); - - try { - Session session = entityManager.unwrap( Session.class ); - - 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\", " + - " ph.valid AS \"ph.valid\" " + - " 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; " - ); - statement.executeUpdate( - "CREATE OR REPLACE PROCEDURE sp_phone_validity ( " + - " validity IN NUMBER, " + - " personPhones OUT SYS_REFCURSOR ) " + - "AS " + - "BEGIN " + - " OPEN personPhones FOR " + - " SELECT phone_number " + - " FROM phone " + - " WHERE valid = validity; " + - "END;" - ); - statement.executeUpdate( - "CREATE OR REPLACE PROCEDURE sp_votes ( " + - " validity IN CHAR, " + - " votes OUT SYS_REFCURSOR ) " + - "AS " + - "BEGIN " + - " OPEN votes FOR " + - " SELECT id " + - " FROM vote " + - " WHERE vote_choice = validity; " + - "END;" - ); - } - finally { - if ( statement != null ) { - statement.close(); - } - } - } ); - } - finally { - entityManager.getTransaction().rollback(); - entityManager.close(); - } - - entityManager = scope.getEntityManagerFactory().createEntityManager(); - entityManager.getTransaction().begin(); - - try { - 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.persist( person1 ); - - Phone phone1 = new Phone( "123-456-7890" ); - phone1.setId( 1L ); - phone1.setValid( true ); - - person1.addPhone( phone1 ); - - Phone phone2 = new Phone( "098_765-4321" ); - phone2.setId( 2L ); - phone2.setValid( false ); - - person1.addPhone( phone2 ); - - entityManager.getTransaction().commit(); - } - finally { - entityManager.close(); - } - } - - @AfterEach - public void destroy(EntityManagerFactoryScope scope) { - EntityManager entityManager = scope.getEntityManagerFactory().createEntityManager(); - entityManager.getTransaction().begin(); - - try { - Session session = entityManager.unwrap( Session.class ); - session.doWork( connection -> { - try (Statement statement = connection.createStatement()) { - statement.executeUpdate( "DROP PROCEDURE sp_count_phones" ); - } - catch (SQLException ignore) { - } - } ); - } - finally { - entityManager.getTransaction().rollback(); - entityManager.close(); - } - - entityManager = scope.getEntityManagerFactory().createEntityManager(); - entityManager.getTransaction().begin(); - - try { - Session session = entityManager.unwrap( Session.class ); - session.doWork( connection -> { - try (Statement statement = connection.createStatement()) { - statement.executeUpdate( "DROP PROCEDURE sp_person_phones" ); - } - catch (SQLException ignore) { - } - } ); - } - finally { - entityManager.getTransaction().rollback(); - entityManager.close(); - } - - entityManager = scope.getEntityManagerFactory().createEntityManager(); - entityManager.getTransaction().begin(); - - try { - Session session = entityManager.unwrap( Session.class ); - session.doWork( connection -> { - try (Statement statement = connection.createStatement()) { - statement.executeUpdate( "DROP FUNCTION fn_count_phones" ); - } - catch (SQLException ignore) { - } - } ); - } - finally { - entityManager.getTransaction().rollback(); - entityManager.close(); - } - scope.releaseEntityManagerFactory(); - } @Test public void testStoredProcedureOutParameter(EntityManagerFactoryScope scope) { @@ -520,4 +300,231 @@ public class OracleStoredProcedureTest { assertEquals( 1, ( (Number) votes.get( 0 ) ).intValue() ); } ); } + + + @BeforeEach + public void prepareSchema(EntityManagerFactoryScope scope) { + scope.inTransaction( (entityManager) -> entityManager.unwrap( Session.class ).doWork( (connection) -> { + try ( Statement 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\", " + + " ph.valid AS \"ph.valid\" " + + " 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; " + ); + statement.executeUpdate( + "CREATE OR REPLACE PROCEDURE sp_phone_validity ( " + + " validity IN NUMBER, " + + " personPhones OUT SYS_REFCURSOR ) " + + "AS " + + "BEGIN " + + " OPEN personPhones FOR " + + " SELECT phone_number " + + " FROM phone " + + " WHERE valid = validity; " + + "END;" + ); + statement.executeUpdate( + "CREATE OR REPLACE PROCEDURE sp_votes ( " + + " validity IN CHAR, " + + " votes OUT SYS_REFCURSOR ) " + + "AS " + + "BEGIN " + + " OPEN votes FOR " + + " SELECT id " + + " FROM vote " + + " WHERE vote_choice = validity; " + + "END;" + ); + + statement.execute( + "create or replace function find_char(in char search, in varchar string, in integer start default 0) " + + "return integer " + + "as begin " + + " select instr( search, string, start ) as position " + + " from dual; " + + " return position; " + + "end" + ); + } + } ) ); + + scope.inTransaction( (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.persist( person1 ); + + Phone phone1 = new Phone( "123-456-7890" ); + phone1.setId( 1L ); + phone1.setValid( true ); + + person1.addPhone( phone1 ); + + Phone phone2 = new Phone( "098_765-4321" ); + phone2.setId( 2L ); + phone2.setValid( false ); + + person1.addPhone( phone2 ); + } ); + } + + @AfterEach + public void cleanUpSchema(EntityManagerFactoryScope scope) { + EntityManager entityManager = scope.getEntityManagerFactory().createEntityManager(); + entityManager.getTransaction().begin(); + + try { + Session session = entityManager.unwrap( Session.class ); + session.doWork( connection -> { + try (Statement statement = connection.createStatement()) { + statement.executeUpdate( "DROP PROCEDURE sp_count_phones" ); + } + catch (SQLException ignore) { + } + } ); + } + finally { + entityManager.getTransaction().rollback(); + entityManager.close(); + } + + entityManager = scope.getEntityManagerFactory().createEntityManager(); + entityManager.getTransaction().begin(); + + try { + Session session = entityManager.unwrap( Session.class ); + session.doWork( connection -> { + try (Statement statement = connection.createStatement()) { + statement.executeUpdate( "DROP PROCEDURE sp_person_phones" ); + } + catch (SQLException ignore) { + } + } ); + } + finally { + entityManager.getTransaction().rollback(); + entityManager.close(); + } + + entityManager = scope.getEntityManagerFactory().createEntityManager(); + entityManager.getTransaction().begin(); + + try { + Session session = entityManager.unwrap( Session.class ); + session.doWork( connection -> { + try (Statement statement = connection.createStatement()) { + statement.executeUpdate( "DROP FUNCTION fn_count_phones" ); + } + catch (SQLException ignore) { + } + } ); + } + finally { + entityManager.getTransaction().rollback(); + entityManager.close(); + } + scope.releaseEntityManagerFactory(); + } + + @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; + String name; + } } diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/procedure/ProcedureParameterTests.java b/hibernate-core/src/test/java/org/hibernate/orm/test/procedure/ProcedureParameterTests.java new file mode 100644 index 0000000000..b147067db5 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/procedure/ProcedureParameterTests.java @@ -0,0 +1,103 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.orm.test.procedure; + +import org.hibernate.annotations.QueryHints; +import org.hibernate.dialect.H2Dialect; + +import org.hibernate.testing.orm.junit.EntityManagerFactoryScope; +import org.hibernate.testing.orm.junit.JiraKey; +import org.hibernate.testing.orm.junit.Jpa; +import org.hibernate.testing.orm.junit.RequiresDialect; +import org.junit.jupiter.api.Test; + +import jakarta.persistence.ParameterMode; +import jakarta.persistence.StoredProcedureQuery; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for registering and binding parameters + * for a {@link jakarta.persistence.StoredProcedureQuery} + * + * Uses H2's `locate` function. Uses the function's default + * parameter to test that not registering the parameter forces + * the parameter's default value to be used + * + * @author Steve Ebersole + */ +@Jpa +@RequiresDialect( H2Dialect.class ) +@JiraKey("https://hibernate.atlassian.net/browse/HHH-11447") +public class ProcedureParameterTests { + + @Test + public void testRegisteredParameter(EntityManagerFactoryScope scope) { + // locate takes 2 parameters with an optional 3rd. Here, we will call it + // registering and binding all 3 parameters + scope.inTransaction( (em) -> { + final StoredProcedureQuery query = em.createStoredProcedureQuery("locate" ); + query.setHint( QueryHints.CALLABLE_FUNCTION, "true" ); + // search-tring + query.registerStoredProcedureParameter( 1, String.class, ParameterMode.IN ); + // source-string + query.registerStoredProcedureParameter( 2, String.class, ParameterMode.IN ); + // start-position + query.registerStoredProcedureParameter( 3, Integer.class, ParameterMode.IN ); + + query.setParameter( 1, "." ); + query.setParameter( 2, "org.hibernate.query" ); + query.setParameter( 3, 5 ); + + final Object singleResult = query.getSingleResult(); + assertThat( singleResult ).isInstanceOf( Integer.class ); + assertThat( singleResult ).isEqualTo( 14 ); + } ); + + // explicit start-position baseline for no-arg + + scope.inTransaction( (em) -> { + final StoredProcedureQuery query = em.createStoredProcedureQuery("locate" ); + query.setHint( QueryHints.CALLABLE_FUNCTION, "true" ); + // search-string + query.registerStoredProcedureParameter( 1, String.class, ParameterMode.IN ); + // source-string + query.registerStoredProcedureParameter( 2, String.class, ParameterMode.IN ); + // start-position + query.registerStoredProcedureParameter( 3, Integer.class, ParameterMode.IN ); + + query.setParameter( 1, "." ); + query.setParameter( 2, "org.hibernate.query" ); + query.setParameter( 3, 0 ); + + final Object singleResult = query.getSingleResult(); + assertThat( singleResult ).isInstanceOf( Integer.class ); + assertThat( singleResult ).isEqualTo( 4 ); + } ); + } + + @Test + public void testUnRegisteredParameter(EntityManagerFactoryScope scope) { + // next, skip start-position registration which should trigger the + // function's default value defined on the database to be applied + scope.inTransaction( (em) -> { + final StoredProcedureQuery query = em.createStoredProcedureQuery("locate" ); + query.setHint( QueryHints.CALLABLE_FUNCTION, "true" ); + // search-string + query.registerStoredProcedureParameter( 1, String.class, ParameterMode.IN ); + // source-string + query.registerStoredProcedureParameter( 2, String.class, ParameterMode.IN ); + + query.setParameter( 1, "." ); + query.setParameter( 2, "org.hibernate.query" ); + + final Object singleResult = query.getSingleResult(); + assertThat( singleResult ).isInstanceOf( Integer.class ); + assertThat( singleResult ).isEqualTo( 4 ); + } ); + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/jpa/procedure/StoredProcedureNullParameterByNameTest.java b/hibernate-core/src/test/java/org/hibernate/test/jpa/procedure/StoredProcedureNullParameterByNameTest.java deleted file mode 100644 index c25291432b..0000000000 --- a/hibernate-core/src/test/java/org/hibernate/test/jpa/procedure/StoredProcedureNullParameterByNameTest.java +++ /dev/null @@ -1,174 +0,0 @@ -/* - * Hibernate, Relational Persistence for Idiomatic Java - * - * License: GNU Lesser General Public License (LGPL), version 2.1 or later. - * See the lgpl.txt file in the root directory or . - */ -package org.hibernate.test.jpa.procedure; - -import java.sql.Connection; -import java.sql.SQLException; -import java.sql.Statement; -import java.util.Map; -import jakarta.persistence.Column; -import jakarta.persistence.Entity; -import jakarta.persistence.EntityManager; -import jakarta.persistence.EntityManagerFactory; -import jakarta.persistence.Id; -import jakarta.persistence.NamedStoredProcedureQueries; -import jakarta.persistence.NamedStoredProcedureQuery; -import jakarta.persistence.ParameterMode; -import jakarta.persistence.StoredProcedureParameter; -import jakarta.persistence.StoredProcedureQuery; -import jakarta.persistence.Table; - -import org.junit.Before; -import org.junit.Test; - -import org.hibernate.dialect.Oracle10gDialect; -import org.hibernate.dialect.OracleDialect; -import org.hibernate.engine.jdbc.connections.spi.JdbcConnectionAccess; -import org.hibernate.engine.jdbc.spi.JdbcServices; -import org.hibernate.engine.spi.SessionFactoryImplementor; -import org.hibernate.orm.test.jpa.BaseEntityManagerFunctionalTestCase; - -import org.hibernate.testing.RequiresDialect; -import org.hibernate.testing.TestForIssue; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.fail; - -/** - * @author Gail Badner - */ -@TestForIssue(jiraKey = "HHH-10761") -@RequiresDialect(OracleDialect.class) -public class StoredProcedureNullParameterByNameTest extends BaseEntityManagerFunctionalTestCase { - EntityManagerFactory entityManagerFactory; - - @Override - protected Class[] getAnnotatedClasses() { - return new Class[] {User.class}; - } - -// @Override -// @SuppressWarnings({ "unchecked" }) -// protected void addConfigOptions(Map options) { -// options.put( org.hibernate.cfg.AvailableSettings.PROCEDURE_NULL_PARAM_PASSING, "true" ); -// } - - @Before - public void startUp() { - entityManagerFactory = getOrCreateEntityManager().getEntityManagerFactory(); - - createProcedures( entityManagerFactory ); - } - - @Test - public void testPassNull() { - EntityManager em = entityManagerFactory.createEntityManager(); - em.getTransaction().begin(); - User user1 = new User(); - user1.id = 1; - user1.name = "aName"; - user1.age = 99; - em.persist( user1 ); - em.getTransaction().commit(); - - em.clear(); - - try { - StoredProcedureQuery query = em.createNamedStoredProcedureQuery( "User.findNameById" ); - query.setParameter( "ID_PARAM", 1 ); - // null is passed for EXTRA_IN_PARAM - - assertEquals( "aName", query.getOutputParameterValue( "NAME_PARAM" ) ); - assertEquals( null, query.getOutputParameterValue( "EXTRA_OUT_PARAM" ) ); - } - finally { - em.close(); - } - } - - - private void createProcedures(EntityManagerFactory emf) { - createProcedure( - emf, - "CREATE OR REPLACE PROCEDURE PROC_EXAMPLE_ONE_BASIC_OUT ( " + - " ID_PARAM IN NUMBER, EXTRA_IN_PARAM IN NUMBER, NAME_PARAM OUT VARCHAR2, EXTRA_OUT_PARAM OUT NUMBER ) " + - "AS " + - "BEGIN " + - " SELECT NAME, EXTRA_IN_PARAM INTO NAME_PARAM, EXTRA_OUT_PARAM FROM USERS WHERE id = ID_PARAM; " + - "END PROC_EXAMPLE_ONE_BASIC_OUT; " - ); - } - - private void createProcedure(EntityManagerFactory emf, String storedProc) { - final SessionFactoryImplementor sf = emf.unwrap( SessionFactoryImplementor.class ); - final JdbcConnectionAccess connectionAccess = sf.getServiceRegistry() - .getService( JdbcServices.class ) - .getBootstrapJdbcConnectionAccess(); - final Connection conn; - try { - conn = connectionAccess.obtainConnection(); - conn.setAutoCommit( false ); - - try { - Statement statement = conn.createStatement(); - - statement.execute( storedProc ); - - try { - statement.close(); - } - catch (SQLException ignore) { - fail(); - } - } - finally { - try { - conn.commit(); - } - catch (SQLException e) { - System.out.println( "Unable to commit transaction after creating creating procedures" ); - fail(); - } - - try { - connectionAccess.releaseConnection( conn ); - } - catch (SQLException ignore) { - fail(); - } - } - } - catch (SQLException e) { - throw new RuntimeException( "Unable to create stored procedures", e ); - } - } - - @NamedStoredProcedureQueries( - @NamedStoredProcedureQuery(name = "User.findNameById", - resultClasses = User.class, - procedureName = "PROC_EXAMPLE_ONE_BASIC_OUT", - parameters = { - @StoredProcedureParameter(mode = ParameterMode.IN, name = "ID_PARAM", type = Integer.class), - @StoredProcedureParameter(mode = ParameterMode.IN, name = "EXTRA_IN_PARAM", type = Integer.class), - @StoredProcedureParameter(mode = ParameterMode.OUT, name = "NAME_PARAM", type = String.class), - @StoredProcedureParameter(mode = ParameterMode.OUT, name = "EXTRA_OUT_PARAM", type = Integer.class) - } - ) - ) - @Entity(name = "User") - @Table(name = "USERS") - public static class User { - @Id - private Integer id; - - @Column(name = "NAME") - private String name; - - @Column(name = "AGE") - private Integer age; - } -}