clean up remaining test suite failures
- stored procedure parameters
This commit is contained in:
parent
2d72015156
commit
e2db993578
|
@ -18,6 +18,7 @@ import java.time.ZoneOffset;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.hibernate.Session;
|
import org.hibernate.Session;
|
||||||
|
import org.hibernate.annotations.QueryHints;
|
||||||
import org.hibernate.dialect.OracleDialect;
|
import org.hibernate.dialect.OracleDialect;
|
||||||
import org.hibernate.procedure.ProcedureCall;
|
import org.hibernate.procedure.ProcedureCall;
|
||||||
import org.hibernate.query.procedure.ProcedureParameter;
|
import org.hibernate.query.procedure.ProcedureParameter;
|
||||||
|
@ -45,6 +46,7 @@ import jakarta.persistence.NamedStoredProcedureQuery;
|
||||||
import jakarta.persistence.ParameterMode;
|
import jakarta.persistence.ParameterMode;
|
||||||
import jakarta.persistence.StoredProcedureParameter;
|
import jakarta.persistence.StoredProcedureParameter;
|
||||||
import jakarta.persistence.StoredProcedureQuery;
|
import jakarta.persistence.StoredProcedureQuery;
|
||||||
|
import org.assertj.core.api.Assertions;
|
||||||
|
|
||||||
import static org.hamcrest.CoreMatchers.instanceOf;
|
import static org.hamcrest.CoreMatchers.instanceOf;
|
||||||
import static org.hamcrest.MatcherAssert.assertThat;
|
import static org.hamcrest.MatcherAssert.assertThat;
|
||||||
|
@ -68,247 +70,25 @@ import static org.junit.jupiter.api.Assertions.fail;
|
||||||
@RequiresDialect(value = OracleDialect.class)
|
@RequiresDialect(value = OracleDialect.class)
|
||||||
public class OracleStoredProcedureTest {
|
public class OracleStoredProcedureTest {
|
||||||
|
|
||||||
@NamedStoredProcedureQueries({
|
@Test
|
||||||
@NamedStoredProcedureQuery(
|
public void testUnRegisteredParameter(EntityManagerFactoryScope scope) {
|
||||||
name = "singleRefCursor",
|
scope.inTransaction( (em) -> {
|
||||||
procedureName = "singleRefCursor",
|
final StoredProcedureQuery function = em.createStoredProcedureQuery( "find_char", Integer.class );
|
||||||
parameters = {
|
function.setHint( QueryHints.CALLABLE_FUNCTION, "true" );
|
||||||
@StoredProcedureParameter(mode = ParameterMode.REF_CURSOR, type = void.class)
|
// search-string
|
||||||
}
|
function.registerStoredProcedureParameter( 1, String.class, ParameterMode.IN );
|
||||||
),
|
// source-string
|
||||||
@NamedStoredProcedureQuery(
|
function.registerStoredProcedureParameter( 2, String.class, ParameterMode.IN );
|
||||||
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
|
function.setParameter( 1, "." );
|
||||||
Long id;
|
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
|
@Test
|
||||||
public void testStoredProcedureOutParameter(EntityManagerFactoryScope scope) {
|
public void testStoredProcedureOutParameter(EntityManagerFactoryScope scope) {
|
||||||
|
@ -520,4 +300,231 @@ public class OracleStoredProcedureTest {
|
||||||
assertEquals( 1, ( (Number) votes.get( 0 ) ).intValue() );
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 );
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
}
|
|
@ -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 <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
|
||||||
*/
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue