How can we call a stored procedure using Hibernate or JPA?
To execute remote procedure use this construction:
Mapping
<sql-query name="RP">
{call some_rp(:param1, :param2)}
</sql-query>
Java code
session.getNamedQuery("RP").setInteger("param1", 1).setInteger("param2", 2).executeUpdate();
One way to call the stored procedure from hibernate
Declare your store procedure inside the @NamedNativeQueries
annotation
//Stock.java
@NamedNativeQueries({
@NamedNativeQuery(
name = "callStockStoreProcedure",
query = "CALL GetStocks(:stockCode)",
resultClass = Stock.class
)
})
@Entity
@Table(name = "stock")
public class Stock implements java.io.Serializable {
// Call it with getNamedQuery().
Query query = session.getNamedQuery("callStockStoreProcedure")
.setParameter("stockCode", "7277");
List result = query.list();
for(int i=0; i<result.size(); i++){
Stock stock = (Stock)result.get(i);
System.out.println(stock.getStockCode());
}
This works
Hibernate provides support for queries via stored procedures and functions. Say for example if we have the following stored procedure,
CREATE OR REPLACE FUNCTION selectAllEmployments
RETURN SYS_REFCURSOR
AS
st_cursor SYS_REFCURSOR;
BEGIN
OPEN st_cursor FOR
SELECT EMPLOYEE, EMPLOYER,
STARTDATE, ENDDATE,
REGIONCODE, EID, VALUE, CURRENCY
FROM EMPLOYMENT;
RETURN st_cursor;
END;
Which return the list of all employees. The stored procedure/function must return a resultset as the first out-parameter to be able to work with Hibernate.
To use the above query in Hibernate you need to map it via a named query.
<sql-query name="selectAllEmployees_SP" callable="true">
<return alias="emp" class="Employment">
<return-property name="employee" column="EMPLOYEE"/>
<return-property name="employer" column="EMPLOYER"/>
<return-property name="startDate" column="STARTDATE"/>
<return-property name="endDate" column="ENDDATE"/>
<return-property name="regionCode" column="REGIONCODE"/>
<return-property name="id" column="EID"/>
<return-property name="salary">
<return-column name="VALUE"/>
<return-column name="CURRENCY"/>
</return-property>
</return>
{ ? = call selectAllEmployments() }
</sql-query>
Rules/limitations for using stored procedures:
{ ? = call functionName(<parameters>) }
or { ? = call procedureName(<parameters>}
. Native call syntax is not supported.For Oracle the following rules apply:
For Sybase or MS SQL server the following rules apply:
Source Ref.: From the official Hibernate docuementation.
Considering the following stored procedure that simply returns a basic return value:
CREATE OR REPLACE PROCEDURE count_comments (
postId IN NUMBER,
commentCount OUT NUMBER )
AS
BEGIN
SELECT COUNT(*) INTO commentCount
FROM post_comment
WHERE post_id = postId;
END;
You can call this one with standard JPA:
StoredProcedureQuery query = entityManager
.createStoredProcedureQuery("count_comments")
.registerStoredProcedureParameter(1, Long.class,
ParameterMode.IN)
.registerStoredProcedureParameter(2, Long.class,
ParameterMode.OUT)
.setParameter(1, 1L);
query.execute();
Long commentCount = (Long) query.getOutputParameterValue(2);
If the stored procedure returns a SYS_REFCURSOR:
CREATE OR REPLACE PROCEDURE post_comments (
postId IN NUMBER,
postComments OUT SYS_REFCURSOR )
AS
BEGIN
OPEN postComments FOR
SELECT *
FROM post_comment
WHERE post_id = postId;
END;
You can call it like this:
StoredProcedureQuery query = entityManager
.createStoredProcedureQuery("post_comments")
.registerStoredProcedureParameter(1, Long.class,
ParameterMode.IN)
.registerStoredProcedureParameter(2, Class.class,
ParameterMode.REF_CURSOR)
.setParameter(1, 1L);
query.execute();
List<Object[]> postComments = query.getResultList();
If you want to call an Oracle database function:
CREATE OR REPLACE FUNCTION fn_count_comments (
postId IN NUMBER )
RETURN NUMBER
IS
commentCount NUMBER;
BEGIN
SELECT COUNT(*) INTO commentCount
FROM post_comment
WHERE post_id = postId;
RETURN( commentCount );
END;
You can't use the StoredProcedureQuery
since it does not work with Hibernate 5, so you can call it like this:
BigDecimal commentCount = (BigDecimal) entityManager
.createNativeQuery(
"SELECT fn_count_comments(:postId) FROM DUAL"
)
.setParameter("postId", 1L)
.getSingleResult();
or with plain JDBC:
Session session = entityManager.unwrap( Session.class );
Integer commentCount = session.doReturningWork( connection -> {
try (CallableStatement function = connection.prepareCall(
"{ ? = call fn_count_comments(?) }" )) {
function.registerOutParameter( 1, Types.INTEGER );
function.setInt( 2, 1 );
function.execute();
return function.getInt( 1 );
}
} );
For more details check out the following articles:
Here is the complete Solution to call a Stored Procedure with Just IN parameters ---
1) Create the Stored Procedure to act on a Table or a Set of Tables:
CREATE OR REPLACE procedure insertHouseHello (
house_date in timestamp,
house_name in varchar2,
house_number in number,
house_value in float)
is
begin
insert into House("HOUSE_DATE","HOUSE_NAME","HOUSE_NUMBER","HOUSE_VALUE")
values ( house_date, house_name,house_number,house_value);
commit;
end;
2) Execute the Stored Procedure from SQL Prompt to check the input. When You call the procedure from Java/Hibernate also You should see the similar result:
exec insertHouseHello(sysdate,'one',123,104);
3) In the Java Code:
log.info("Now trying to call the Stored Procedure*****************");
Query exQuery = session.createSQLQuery("CALL " +
"insertHouseHello(:timestmp,:hname,:hno,:hvalue)");
exQuery.setParameter("timestmp",
new java.sql.Timestamp(Calendar.getInstance().getTime().getTime()));
exQuery.setParameter("hname", 34);
exQuery.setParameter("hno", 212);
exQuery.setParameter("hvalue", 12);
int exRows = exQuery.executeUpdate();
log.info("Executed Rows from Stored Procedure****************"+exRows);
4) Now check the result in the Table, that should get updated accordingly:
one approach can be with getNamedQuery().
Query query = session.getNamedQuery("callStockStoreProcedure")
.setParameter("stockCode", "7277");
List result = query.list();
for(int i=0; i<result.size(); i++){
Stock stock = (Stock)result.get(i);
System.out.println(stock.getStockCode());
}
you have to map or use annotations
there are others: source