Close

JPA - Calling Stored Procedure With Ref Cursor Output Parameter

[Last Updated: Sep 12, 2018]

This example shows how to call Oracle stored procedures with ref_cursor out parameter.

Following method of EntityManager can be used to create an instance of StoredProcedureQuery with resultClasses argument to map records returned by a database cursor:

StoredProcedureQuery createStoredProcedureQuery(String procedureName, Class... resultClasses)

Quick example

CREATE OR REPLACE PROCEDURE myStoredProcedureName(my_cursor OUT SYS_REFCURSOR)
IS
BEGIN
  OPEN my_cursor FOR SELECT * FROM MY_TABLE;
END;
 StoredProcedureQuery procedureQuery = 
                        entityManager.createStoredProcedureQuery("myStoredProcedureName", ResultType.class);
 procedureQuery.registerStoredProcedureParameter("my_cursor", void.class, ParameterMode.REF_CURSOR);
 procedureQuery.execute();
 List<ResultType> resultList = procedureQuery.getResultList();

Example

Follow this guide to install Oracle and SQL Developer if you already don't have it. (Also see this example of how to connect oracle database in JPA).

pom.xml

<project .....>
<modelVersion>4.0.0</modelVersion>

<groupId>com.logicbig.example</groupId>
<artifactId>jpa-stored-procedure-with-oracle-ref-cursor</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>5.3.6.Final</version>
</dependency>
<dependency>
<groupId>com.oracle</groupId>
<artifactId>ora-jdbc</artifactId>
<version>7</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
</plugins>
</build>

</project>

You also have to install Oracle JDBC driver locally for above ora-jdbc dependency to resolve.

Creating database tables and procedures

Copy paste following script to Oracle SQL Developer and execute it.

src/main/resources/db.sql

DROP TABLE PERSON;
DROP SEQUENCE SQ_PERSON;

CREATE TABLE PERSON(
 ID NUMBER(19),
 FIRST_NAME VARCHAR(255),
 LAST_NAME VARCHAR(255),
 ADDRESS VARCHAR(255),
 PRIMARY KEY (ID)
);

CREATE SEQUENCE SQ_PERSON MINVALUE 1 START WITH 1 INCREMENT BY 1 CACHE 10;
DROP TABLE PERSON_HISTORY;
CREATE TABLE PERSON_HISTORY AS SELECT * FROM PERSON WHERE 1=0;

CREATE OR REPLACE PROCEDURE MOVE_TO_HISTORY(person_id_in IN NUMBER, msg_out OUT VARCHAR2)
IS
 temp_count NUMBER := -1;
BEGIN
   select count(*) into temp_count from PERSON p where p.id = person_id_in;
  IF temp_count > -1  THEN
      insert into PERSON_HISTORY (select * from PERSON where id = person_id_in);
      msg_out := 'Person with id: ' || person_id_in || ' moved to History table. Update count: ' || sql%Rowcount;
      delete from PERSON p where p.id = person_id_in;
  ELSE
   msg_out := 'No Person Exists with id: '|| person_id_in;
 END IF;
END;
/

CREATE OR REPLACE PROCEDURE FETCH_PERSON_HISTORY(history_cursor OUT SYS_REFCURSOR)
IS
BEGIN
  OPEN history_cursor FOR SELECT * FROM PERSON_HISTORY;
END;

The stored procedure 'MOVE_TO_HISTORY' moves a person row from 'PERSON' table to 'PERSON_HISTORY' table. The procedure 'FETCH_PERSON_HISTORY' populates the history_cursor with the records fetched from PERSON_HISTORY.

Creating JPA application

src/main/resources/META-INF/persistence.xml

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.1"
             xmlns="http://xmlns.jcp.org/xml/ns/persistence"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd">

    <persistence-unit name="example-unit" transaction-type="RESOURCE_LOCAL">
        <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
        <exclude-unlisted-classes>false</exclude-unlisted-classes>
        <properties>
            <property name="javax.persistence.jdbc.driver" value="oracle.jdbc.OracleDriver"/>
            <property name="javax.persistence.jdbc.url" value="jdbc:oracle:thin:@localhost:1521:xe"/>
            <property name="javax.persistence.jdbc.user" value="system"/>
            <property name="javax.persistence.jdbc.password" value="1111"/>
        </properties>
    </persistence-unit>
</persistence>

Entity

@Entity
public class Person {
    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "SQ_PERSON")
    @SequenceGenerator(sequenceName = "SQ_PERSON", allocationSize = 1, name = "SQ_PERSON")
    private long id;
    @Column(name = "FIRST_NAME")
    private String firstName;
    @Column(name = "LAST_NAME")
    private String lastName;
    private String address;
    .............
}

Calling stored procedures

public class ExampleMain {
    private static EntityManagerFactory entityManagerFactory =
            Persistence.createEntityManagerFactory("example-unit");

    public static void main(String[] args) {
        try {
            reset();
            persistEntities();
            findAllEmployeeEntities();
            movePersonToHistoryByName("Dana");
            movePersonToHistoryByName("Mike");
            fetchPersonHistory();

        } finally {
            entityManagerFactory.close();
        }
    }

    //calling procedure with ref_cursor
    private static void fetchPersonHistory() {
        System.out.println("-- Fetching person History --");
        EntityManager entityManager = entityManagerFactory.createEntityManager();
        StoredProcedureQuery procedureQuery = entityManager.createStoredProcedureQuery("FETCH_PERSON_HISTORY",
                Person.class);//PERSON_HISTORY table also has same fields
        procedureQuery.registerStoredProcedureParameter("history_cursor", void.class, ParameterMode.REF_CURSOR);
        procedureQuery.execute();
        @SuppressWarnings("unchecked")
        List<Person> resultList = procedureQuery.getResultList();
        //without Person.class arg above list of object array will be returned
        resultList.forEach(System.out::println);
    }

    private static void movePersonToHistoryByName(String name) {
        System.out.printf("-- Moving person to history table name: %s --%n", name);
        EntityManager entityManager = entityManagerFactory.createEntityManager();
        //get person id
        TypedQuery<Long> query = entityManager
                .createQuery("SELECT p.id FROM Person p WHERE p.firstName = :firstName", Long.class);
        query.setParameter("firstName", name);
        Long personId = query.getSingleResult();
        //stored procedure
        StoredProcedureQuery procedureQuery = entityManager
                .createStoredProcedureQuery("MOVE_TO_HISTORY");
        procedureQuery.registerStoredProcedureParameter("person_id_in", Long.class, ParameterMode.IN);
        procedureQuery.registerStoredProcedureParameter("msg_out", String.class, ParameterMode.OUT);
        entityManager.getTransaction().begin();
        procedureQuery.setParameter("person_id_in", personId);
        procedureQuery.execute();
        Object msg_out = procedureQuery.getOutputParameterValue("msg_out");
        System.out.println("Out msg= " + msg_out);
        entityManager.getTransaction().commit();
    }

    private static void findAllEmployeeEntities() {
        System.out.println("-- finding Person entities -  --");
        EntityManager em = entityManagerFactory.createEntityManager();
        TypedQuery<Person> query = em.createQuery("SELECT p from Person p", Person.class);
        List<Person> resultList = query.getResultList();
        resultList.forEach(System.out::println);
        em.close();
    }

    private static void persistEntities() {
        Person person1 = Person.create("Dana", "Whitley", "464 Gorsuch Drive");
        Person person2 = Person.create("Robin", "Cash", "64 Zella Park");
        Person person3 = Person.create("Chary", "Mess", "112 Yellow Hill");
        Person person4 = Person.create("Rose", "Kantata", "2736 Kooter Lane");
        Person person5 = Person.create("Mike","Togglie", "111 Cool Dr");
        EntityManager em = entityManagerFactory.createEntityManager();
        em.getTransaction().begin();
        em.persist(person1);
        em.persist(person2);
        em.persist(person3);
        em.persist(person4);
        em.persist(person5);
        em.getTransaction().commit();
        em.close();
    }

    private static void reset() {
        EntityManager em = entityManagerFactory.createEntityManager();
        em.getTransaction().begin();
        Query query = em.createQuery("DELETE FROM Person");
        query.executeUpdate();
        query = em.createNativeQuery("DELETE from PERSON_HISTORY");
        query.executeUpdate();
        em.getTransaction().commit();
    }
}
-- finding Person entities -  --
Person{id=66, firstName='Dana', lastName='Whitley', address='464 Gorsuch Drive'}
Person{id=67, firstName='Robin', lastName='Cash', address='64 Zella Park'}
Person{id=68, firstName='Chary', lastName='Mess', address='112 Yellow Hill'}
Person{id=69, firstName='Rose', lastName='Kantata', address='2736 Kooter Lane'}
Person{id=70, firstName='Mike', lastName='Togglie', address='111 Cool Dr'}
-- Moving person to history table name: Dana --
Out msg= Person with id: 66 moved to History table. Update count: 1
-- Moving person to history table name: Mike --
Out msg= Person with id: 70 moved to History table. Update count: 1
-- Fetching person History --
Person{id=66, firstName='Dana', lastName='Whitley', address='464 Gorsuch Drive'}
Person{id=70, firstName='Mike', lastName='Togglie', address='111 Cool Dr'}

Example Project

Dependencies and Technologies Used:

  • hibernate-core 5.3.6.Final: Hibernate's core ORM functionality.
    Implements javax.persistence:javax.persistence-api version 2.2
  • ora-jdbc 7: POM was created from install:install-file.
  • JDK 1.8
  • Maven 3.5.4

Calling Oracle Stored Procedure involving Ref Cursor Select All Download
  • jpa-stored-procedure-with-oracle-ref-cursor
    • src
      • main
        • java
          • com
            • logicbig
              • example
                • ExampleMain.java
          • resources
            • META-INF

    See Also