How to call a stored procedure from Java and JPA - java

I am writing a simple web application to call a stored procedure and retrieve some data.
Its a very simple application, which interacts with client's database. We pass employee id and company id and the stored procedure will return employee details.
Web application cannot update/delete data and is using SQL Server.
I am deploying my web application in Jboss AS. Should I use JPA to access the stored procedure or CallableStatement. Any advantage of using JPA in this case.
Also what will be the sql statement to call this stored procedure. I have never used stored procedures before and I am struggling with this one. Google was not much of a help.
Here is the stored procedure:
CREATE procedure getEmployeeDetails (#employeeId int, #companyId int)
as
begin
select firstName,
lastName,
gender,
address
from employee et
where et.employeeId = #employeeId
and et.companyId = #companyId
end
Update:
For anyone else having problem calling stored procedure using JPA.
Query query = em.createNativeQuery("{call getEmployeeDetails(?,?)}",
EmployeeDetails.class)
.setParameter(1, employeeId)
.setParameter(2, companyId);
List<EmployeeDetails> result = query.getResultList();
Things I have noticed:
Parameter names didn't work for me, so try using parameter index.
Correct sql statement {call sp_name(?,?)} instead of call sp_name(?,?)
If stored procedure is returning a result set, even if you know with only one row, getSingleResult wont work
Pass a resultSetMapping name or result class details

JPA 2.1 now support Stored Procedure, read the Java doc here.
Example:
StoredProcedureQuery storedProcedure = em.createStoredProcedureQuery("sales_tax");
// set parameters
storedProcedure.registerStoredProcedureParameter("subtotal", Double.class, ParameterMode.IN);
storedProcedure.registerStoredProcedureParameter("tax", Double.class, ParameterMode.OUT);
storedProcedure.setParameter("subtotal", 1f);
// execute SP
storedProcedure.execute();
// get result
Double tax = (Double)storedProcedure.getOutputParameterValue("tax");
See detailed example here.

I am deploying my web application in Jboss AS. Should I use JPA to access the stored procedure or CallableStatement. Any advantage of using JPA in this case.
It is not really supported by JPA but it's doable. Still I wouldn't go this way:
using JPA just to map the result of a stored procedure call in some beans is really overkill,
especially given that JPA is not really appropriate to call stored procedure (the syntax will be pretty verbose).
I would thus rather consider using Spring support for JDBC data access, or a data mapper like MyBatis or, given the simplicity of your application, raw JDBC and CallableStatement. Actually, JDBC would probably be my choice. Here is a basic kickoff example:
CallableStatement cstmt = con.prepareCall("{call getEmployeeDetails(?, ?)}");
cstmt.setInt("employeeId", 123);
cstmt.setInt("companyId", 456);
ResultSet rs = cstmt.executeQuery();
Reference
JDBC documentation: Java SE 6

You need to pass the parameters to the stored procedure.
It should work like this:
List result = em
.createNativeQuery("call getEmployeeDetails(:employeeId,:companyId)")
.setParameter("emplyoyeeId", 123L)
.setParameter("companyId", 456L)
.getResultList();
Update:
Or maybe it shouldn't.
In the Book EJB3 in Action, it says on page 383, that JPA does not support stored procedures (page is only a preview, you don't get the full text, the entire book is available as a download in several places including this one, I don't know if this is legal though).
Anyway, the text is this:
JPA and database stored procedures
If you’re a big fan of SQL, you may be
willing to exploit the power of
database stored procedures.
Unfortunately, JPA doesn’t support
stored procedures, and you have to
depend on a proprietary feature of
your persistence provider. However,
you can use simple stored functions
(without out parameters) with a native
SQL query.

For a simple stored procedure that using IN/OUT parameters like this
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 it from JPA as follows:
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);
For a stored procedure which uses a SYS_REFCURSOR OUT parameter:
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 as follows:
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();
For a SQL function that looks as follows:
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 call it like this:
BigDecimal commentCount = (BigDecimal) entityManager
.createNativeQuery(
"SELECT fn_count_comments(:postId) FROM DUAL"
)
.setParameter("postId", 1L)
.getSingleResult();
At least when using Hibernate 4.x and 5.x because the JPA StoredProcedureQuery does not work for SQL FUNCTIONS.
For more details about how to call stored procedures and functions when using JPA and Hibernate, check out the following articles
How to call Oracle stored procedures and functions with JPA and Hibernate
How to call SQL Server stored procedures and functions with JPA and Hibernate
How to call PostgreSQL functions (stored procedures) with JPA and Hibernate
How to call MySQL stored procedures and functions with JPA and Hibernate

How to retrieve Stored Procedure output parameter using JPA (2.0 needs EclipseLink imports and 2.1 does not)
Even though this answer does elaborate on returning a recordset from a stored procedure,
I am posting here, because it took me ages to figure it out and this thread helped me.
My application was using Eclipselink-2.3.1, but I will force an upgrade to
Eclipselink-2.5.0, as JPA 2.1 has much better support for stored procedures.
Using EclipseLink-2.3.1/JPA-2.0: Implementation-Dependent
This method requires imports of EclipseLink classes from "org.eclipse.persistence", so it is specific to Eclipselink implementation.
I found it at "http://www.yenlo.nl/en/calling-oracle-stored-procedures-from-eclipselink-with-multiple-out-parameters".
StoredProcedureCall storedProcedureCall = new StoredProcedureCall();
storedProcedureCall.setProcedureName("mypackage.myprocedure");
storedProcedureCall.addNamedArgument("i_input_1"); // Add input argument name.
storedProcedureCall.addNamedOutputArgument("o_output_1"); // Add output parameter name.
DataReadQuery query = new DataReadQuery();
query.setCall(storedProcedureCall);
query.addArgument("i_input_1"); // Add input argument names (again);
List<Object> argumentValues = new ArrayList<Object>();
argumentValues.add("valueOf_i_input_1"); // Add input argument values.
JpaEntityManager jpaEntityManager = (JpaEntityManager) getEntityManager();
Session session = jpaEntityManager.getActiveSession();
List<?> results = (List<?>) session.executeQuery(query, argumentValues);
DatabaseRecord record = (DatabaseRecord) results.get(0);
String result = String.valueOf(record.get("o_output_1")); // Get output parameter
Using EclipseLink-2.5.0/JPA-2.1: Implementation-Independent (documented already in this thread)
This method is implementation independent (don't need Eclipslink imports).
StoredProcedureQuery query = getEntityManager().createStoredProcedureQuery("mypackage.myprocedure");
query.registerStoredProcedureParameter("i_input_1", String.class, ParameterMode.IN);
query.registerStoredProcedureParameter("o_output_1", String.class, ParameterMode.OUT);
query.setParameter("i_input_1", "valueOf_i_input_1");
boolean queryResult = query.execute();
String result = String.valueOf(query.getOutputParameterValue("o_output_1"));

For me, only the following worked with Oracle 11g and Glassfish 2.1 (Toplink):
Query query = entityManager.createNativeQuery("BEGIN PROCEDURE_NAME(); END;");
query.executeUpdate();
The variant with curly braces resulted in ORA-00900.

If using EclipseLink you can use the #NamedStoredProcedureQuery or StoreProcedureCall to execute any stored procedure, including ones with output parameters, or out cursors. Support for stored functions and PLSQL data-types is also available.
See,
http://en.wikibooks.org/wiki/Java_Persistence/Advanced_Topics#Stored_Procedures

The following works for me:
Query query = em.createNativeQuery("BEGIN VALIDACIONES_QPAI.RECALC_COMP_ASSEMBLY('X','X','X',0); END;");
query.executeUpdate();

May be it's not the same for Sql Srver but for people using oracle and eclipslink it's working for me
ex: a procedure that have one IN param (type CHAR) and two OUT params (NUMBER & VARCHAR)
in the persistence.xml declare the persistence-unit :
<persistence-unit name="presistanceNameOfProc" transaction-type="RESOURCE_LOCAL">
<provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
<jta-data-source>jdbc/DataSourceName</jta-data-source>
<mapping-file>META-INF/eclipselink-orm.xml</mapping-file>
<properties>
<property name="eclipselink.logging.level" value="FINEST"/>
<property name="eclipselink.logging.logger" value="DefaultLogger"/>
<property name="eclipselink.weaving" value="static"/>
<property name="eclipselink.ddl.table-creation-suffix" value="JPA_STORED_PROC" />
</properties>
</persistence-unit>
and declare the structure of the proc in the eclipselink-orm.xml
<?xml version="1.0" encoding="UTF-8"?><entity-mappings version="2.0"
xmlns="http://java.sun.com/xml/ns/persistence/orm" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence/orm orm_2_0.xsd">
<named-stored-procedure-query name="PERSIST_PROC_NAME" procedure-name="name_of_proc" returns-result-set="false">
<parameter direction="IN" name="in_param_char" query-parameter="in_param_char" type="Character"/>
<parameter direction="OUT" name="out_param_int" query-parameter="out_param_int" type="Integer"/>
<parameter direction="OUT" name="out_param_varchar" query-parameter="out_param_varchar" type="String"/>
</named-stored-procedure-query>
in the code you just have to call your proc like this :
try {
final Query query = this.entityManager
.createNamedQuery("PERSIST_PROC_NAME");
query.setParameter("in_param_char", 'V');
resultQuery = (Object[]) query.getSingleResult();
} catch (final Exception ex) {
LOGGER.log(ex);
throw new TechnicalException(ex);
}
to get the two output params :
Integer myInt = (Integer) resultQuery[0];
String myStr = (String) resultQuery[1];

This worked for me.
#Entity
#Table(name="acct")
#NamedNativeQueries({
#NamedNativeQuery(callable=true, name="Account.findOne", query="call sp_get_acct(?), resultClass=Account.class)})
public class Account{
// Code
}
Note : in future if you decide to use default version of findOne then just comment the NamedNativeQueries annotation and JPA will switch to default

This answer might be helpful if you have entity manager
I had a stored procedure to create next number and on server side I have seam framework.
Client side
Object on = entityManager.createNativeQuery("EXEC getNextNmber").executeUpdate();
log.info("New order id: " + on.toString());
Database Side (SQL server) I have stored procedure named getNextNmber

You can use #Query(value = "{call PROC_TEST()}", nativeQuery = true) in your repository. This worked for me.
Attention: use '{' and '}' or else it will not work.

JPA 2.0 doesn't support RETURN values, only calls.
My solution was. Create a FUNCTION calling PROCEDURE.
So, inside JAVA code you execute a NATIVE QUERY calling the oracle FUNCTION.

From JPA 2.1 , JPA supports to call stored procedures using the dynamic StoredProcedureQuery, and the declarative #NamedStoredProcedureQuery.

To call stored procedure we can use Callable Statement in java.sql package.

Try this code:
return em.createNativeQuery("{call getEmployeeDetails(?,?)}",
EmployeeDetails.class)
.setParameter(1, employeeId)
.setParameter(2, companyId).getResultList();

persistence.xml
<persistence-unit name="PU2" transaction-type="RESOURCE_LOCAL">
<non-jta-data-source>jndi_ws2</non-jta-data-source>
<exclude-unlisted-classes>false</exclude-unlisted-classes>
<properties/>
codigo java
String PERSISTENCE_UNIT_NAME = "PU2";
EntityManagerFactory factory2;
factory2 = Persistence.createEntityManagerFactory(PERSISTENCE_UNIT_NAME);
EntityManager em2 = factory2.createEntityManager();
boolean committed = false;
try {
try {
StoredProcedureQuery storedProcedure = em2.createStoredProcedureQuery("PKCREATURNO.INSERTATURNO");
// set parameters
storedProcedure.registerStoredProcedureParameter("inuPKEMPRESA", BigDecimal.class, ParameterMode.IN);
storedProcedure.registerStoredProcedureParameter("inuPKSERVICIO", BigDecimal.class, ParameterMode.IN);
storedProcedure.registerStoredProcedureParameter("inuPKAREA", BigDecimal.class, ParameterMode.IN);
storedProcedure.registerStoredProcedureParameter("isbCHSIGLA", String.class, ParameterMode.IN);
storedProcedure.registerStoredProcedureParameter("INUSINCALIFICACION", BigInteger.class, ParameterMode.IN);
storedProcedure.registerStoredProcedureParameter("INUTIMBRAR", BigInteger.class, ParameterMode.IN);
storedProcedure.registerStoredProcedureParameter("INUTRANSFERIDO", BigInteger.class, ParameterMode.IN);
storedProcedure.registerStoredProcedureParameter("INTESTADO", BigInteger.class, ParameterMode.IN);
storedProcedure.registerStoredProcedureParameter("inuContador", BigInteger.class, ParameterMode.OUT);
BigDecimal inuPKEMPRESA = BigDecimal.valueOf(1);
BigDecimal inuPKSERVICIO = BigDecimal.valueOf(5);
BigDecimal inuPKAREA = BigDecimal.valueOf(23);
String isbCHSIGLA = "";
BigInteger INUSINCALIFICACION = BigInteger.ZERO;
BigInteger INUTIMBRAR = BigInteger.ZERO;
BigInteger INUTRANSFERIDO = BigInteger.ZERO;
BigInteger INTESTADO = BigInteger.ZERO;
BigInteger inuContador = BigInteger.ZERO;
storedProcedure.setParameter("inuPKEMPRESA", inuPKEMPRESA);
storedProcedure.setParameter("inuPKSERVICIO", inuPKSERVICIO);
storedProcedure.setParameter("inuPKAREA", inuPKAREA);
storedProcedure.setParameter("isbCHSIGLA", isbCHSIGLA);
storedProcedure.setParameter("INUSINCALIFICACION", INUSINCALIFICACION);
storedProcedure.setParameter("INUTIMBRAR", INUTIMBRAR);
storedProcedure.setParameter("INUTRANSFERIDO", INUTRANSFERIDO);
storedProcedure.setParameter("INTESTADO", INTESTADO);
storedProcedure.setParameter("inuContador", inuContador);
// execute SP
storedProcedure.execute();
// get result
try {
long _inuContador = (long) storedProcedure.getOutputParameterValue("inuContador");
varCon = _inuContador + "";
} catch (Exception e) {
}
} finally {
}
} finally {
em2.close();
}

the simplest way is to use JpaRepository
1- Create a stored procedure
CREATE PROCEDURE dbo.getEmployeeDetails
(
#employeeId int,
#companyId int
) AS
BEGIN
SELECT firstName,lastName,gender,address
FROM employee et
WHERE et.employeeId = #employeeId and et.companyId = #companyId
END
2- Create Entity
#Getter
#Setter
#ToString
#NoArgsConstructor
#AllArgsConstructor
#Entity
public class EmployeeDetails {
#Id
private String firstName;
private String lastName;
private String gender;
private String address;
}
3- Create Repository
public interface EmployeeDetailsRepository extends
JpaRepository<EmployeeDetails,String> {
#Query(value = "EXEC dbo.getEmployeeDetails #employeeId=:empId,
#companyId=:compId",nativeQuery =true)
List<EmployeeDetails> getEmployeeList(#Param("employeeId") Integer empId,
#Param("companyId") Integer compId);
}
4- create Controller
#CrossOrigin(origins = "*")
#RestController
#RequestMapping(value = "/api/employee")
public class EmployeeController {
#Autowired
private EmployeeDetailsRepository empRepo;
#GetMapping(value = "/details")
public ResponseEntity<List<EmployeeDetails>> getEmployeeDetails(#RequestParam
String empId, #RequestParam String compId) {
try {
List<EmployeeDetails> result = empRepo.getEmployeeList(
Integer.valueOf(empId),Integer.valueOf(compId));
return ResponseEntity.status(HttpStatus.OK).body(result);
}
catch (Exception ex)
{
return ResponseEntity.status(HttpStatus.EXPECTATION_FAILED).body(null);
}
}
}
you can now call http://localhost:8080/api/employee/details?empId=1&compId=25

If you're not too attached to calling this particular procedure with JPA or JDBC, you could use jOOQ, a third party library that generates stubs for all of your stored procedures to simplify calling them, and making the calls type safe.
Calling procedures returning unspecified cursors
In your particular case, the procedure returns an untyped, undeclared cursor (it could return several cursors and interleaved update counts). So, you could call the procedure like this with jOOQ:
GetEmployeeDetails proc = new GetEmployeeDetails();
proc.setEmployeeId(1);
proc.setCompanyId(2);
proc.execute(configuration);
// Iterate over potentially multiple results
for (Result<?> result : proc.getResults()) {
// Print the first result set (your employee query)
System.out.println(result);
// Use your implicit knowledge of the content of the query
// Without type safety
for (Record record : result) {
// All tables / columns are also generated
System.out.println("First name: " + record.get(EMPLOYEE.FIRSTNAME));
System.out.println("Last name: " + record.get(EMPLOYEE.LASTNAME));
System.out.println("Gender: " + record.get(EMPLOYEE.GENDER));
System.out.println("Address: " + record.get(EMPLOYEE.ADDRESS));
}
}
Using an actual table valued function, instead
Personally, I don't really like that feature of a few RDBMS (including SQL Server, MySQL) of returning arbitrary untyped cursors. Why not just declare the result type? SQL Server has powerful table valued functions. E.g. just use this syntax here:
CREATE FUNCTION getEmployeeDetails (#employeeId int, #companyId int)
RETURNS TABLE
AS RETURN
SELECT
firstName,
lastName,
gender,
address
FROM employee et
WHERE et.employeeId = #employeeId
AND et.companyId = #companyId
Now, you have the full type information associated with this function in your catalog, and if you're still using jOOQ, that information will be available to the code generator, so you can call the function like this:
for (GetEmployeeDetailsRecord record : ctx.selectFrom(getEmployeeDetails(1, 2))) {
System.out.println("First name: " + record.getFirstName());
System.out.println("Last name: " + record.getLastName());
System.out.println("Gender: " + record.getGender());
System.out.println("Address: " + record.getAddress());
}
Disclaimer: I work for the company behind jOOQ

Related

Postgres stored procedure call from java, table to return

I made a Postgres stored procedure:
CREATE OR REPLACE FUNCTION GetUser(ipUserId integer)
RETURNS setof users AS $$
BEGIN
IF ipUserId is null THEN
return query select * from users A order by modifieddate desc;
END IF;
return query select * from users where iduser = ipUserId;
END;
$$ LANGUAGE plpgsql;
I tried to use it in java like this:
StoredProcedureQuery query = entityManager.createStoredProcedureQuery("GetUser").
registerStoredProcedureParameter("ipUserId",
Integer.class, ParameterMode.IN)
.registerStoredProcedureParameter("users",
List.class, ParameterMode.OUT)
.setParameter("postId", 1);
or
StoredProcedureQuery query = entityManager.createStoredProcedureQuery("GetUser")
.registerStoredProcedureParameter(1,void.class, ParameterMode.REF_CURSOR)
.registerStoredProcedureParameter(2,Integer.class, ParameterMode.IN)
.setParameter(2, ipIdUser);
I want to store the result in a List.
What and how should i do, because i'm getting all kind of errors?
Update :
Those are the errors :
javax.persistence.PersistenceException: org.hibernate.exception.SQLGrammarException: Error calling CallableStatement.getMoreResults
Caused by: org.hibernate.exception.SQLGrammarException: Error calling CallableStatement.getMoreResults
at org.hibernate.exception.internal.SQLStateConversionDelegate.convert(SQLStateConversionDelegate.java:106)
at org.hibernate.exception.internal.StandardSQLExceptionConverter.convert(StandardSQLExceptionConverter.java:42)
at org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:111)
at org.hibernate.result.internal.OutputsImpl.convert(OutputsImpl.java:79)
at org.hibernate.result.internal.OutputsImpl.<init>(OutputsImpl.java:56)
at org.hibernate.procedure.internal.ProcedureOutputsImpl.<init>(ProcedureOutputsImpl.java:34)
at org.hibernate.procedure.internal.ProcedureCallImpl.buildOutputs(ProcedureCallImpl.java:453)
at org.hibernate.procedure.internal.ProcedureCallImpl.getOutputs(ProcedureCallImpl.java:404)
at org.hibernate.procedure.internal.ProcedureCallImpl.outputs(ProcedureCallImpl.java:663)
at org.hibernate.procedure.internal.ProcedureCallImpl.getResultList(ProcedureCallImpl.java:751)
... 21 more
Caused by: org.postgresql.util.PSQLException: A CallableStatement was executed with an invalid number of parameters
You can try using CallableStatement.
Assuming that your Connection var is OK:
CallableStatement stmt = con.prepareCall("{call SCHEMA.PROCEDURE_NAME (?, ?)}");
stmt.setInt(1, custom_var);
stmt.registerOutParameter(2, OracleTypes.INTEGER);
stmt.execute();
To get result: stmt.getInt(3); stmt.getString(4)
If you can't succeed try using JdbcTemplate:
SimpleJdbcCall call = new SimpleJdbcCall(this.jdbcTemplate).withSchemaName(SCHEMA).withProcedureName(PROC);
MapSqlParameterSource params = new MapSqlParameterSource();
params.addValue("ipUserId", custom_var);
Map out = call.execute(params);
To get single result: Integer.parseInt("" + out.get("OUT_PARAM_NAME")); (String) out.get("OUT_PARAM_NAME2"));
Or you can save all the result in a list to work on it later:
SimpleJdbcCall call = new SimpleJdbcCall(this.jdbcTemplate);
List<Map<String, Object>> rows = call.getJdbcTemplate().queryForList(PROC_STRING, new Object[] { param1, param2 });
I found a much simple solution, just make a SQL Query to call the procedure with hibernate.
String SqlString = "select * from GetUser({0})";
if (ipIdUser == null )
SqlString = MessageFormat.format(SqlString, "NULL");
else
SqlString = MessageFormat.format(SqlString, ipIdUser);
LOGGER.info("SqlSting =" + SqlString);
return entityManager.createNativeQuery(SqlString, User.class)
.getResultList();
Why not use getResultList on StoredProcedureQuery? This avoids having to do the string manipulation.
List<User> users = entityManager.createStoredProcedureQuery("GetUser")
.registerStoredProcedureParameter("ipUserId", Integer.class, ParameterMode.IN)
.setParameter("ipUserId", ipIdUser)
.getResultList();
In your case, you have a table valued function that happens to map to an actual table, so the approach using the JPA native query is elegant too, but perhaps your projection is something different, or you return multiple nested data structures, arrays, etc, where working with JPA might be tricky.
jOOQ generates code for all of your procedures, including table valued functions. For example, you'll have a Routines class containing a getUser method, which you can query like this:
Result<GetUser> result = Routines.getUser(configuration, 1);
You can also use the table valued function as an actual table in the query, like this:
Result<GetUser> result = ctx.selectFrom(GETUSER(1)).fetch();
Disclaimer: I work for the company behind jOOQ.

The JPA createStoredProcedureQuery throws "ORA-01000: maximum open cursors exceeded" when using Hibernate

I try to call Oracle stored procedure using "createStoredProcedureQuery" of EntityManager by this way:
#Transactional(readOnly = false, propagation = Propagation.REQUIRED, isolation = Isolation.READ_COMMITTED)
public void saveMeterVol(Meter meter, Double vol1, Chng chng, User user, Date dt1, Date dt2) {
StoredProcedureQuery qr = em.createStoredProcedureQuery("mt.P_METER.meter_vol_ins_upd_java");
qr.registerStoredProcedureParameter(1, Integer.class, ParameterMode.OUT);
qr.registerStoredProcedureParameter(2, Integer.class, ParameterMode.IN);
qr.registerStoredProcedureParameter(3, Integer.class, ParameterMode.IN);
qr.registerStoredProcedureParameter(4, Double.class, ParameterMode.IN);
qr.registerStoredProcedureParameter(5, Date.class, ParameterMode.IN);
qr.registerStoredProcedureParameter(6, Date.class, ParameterMode.IN);
qr.registerStoredProcedureParameter(7, String.class, ParameterMode.IN);
qr.setParameter(2, meter.getId());
qr.setParameter(3, chng.getId());
qr.setParameter(4, vol1);
qr.setParameter(5, dt1);
qr.setParameter(6, dt2);
qr.setParameter(7, user.getCd());
qr.execute();
}
When I call this method over 300 times, Oracle fall into exception:
ORA-01000: maximum open cursors exceeded
As I understand, Java doesn't close Oracle cursor after call my procedure, but I don't understand why?
I tried to do
em.close();
but it didn't help.
I use:
<spring-framework.version>5.0.5.RELEASE</spring-framework.version>
<hibernate.version>5.1.0.Final</hibernate.version>
<java.version>1.8</java.version>
The default CallableStatement handling mechanism
When calling the execute method on the JPA StoredProcedureQuery or outputs().getCurrent() on the Hibernate ProcedureCall, Hibernate executes the following actions:
Notice that a JDBC CallableStatement is prepared and stored in the associated ProcedureOutputsImpl object. When calling the getOutputParameterValue method, Hibernate will use the underlying CallableStatement to fetch the OUT parameter.
For this reason, the underlying JDBC CallableStatement remains open even after executing the stored procedure and fetching the OUT or REF_CURSOR parameters.
Now, by default, the CallableStatement is closed upon ending the currently running database transaction, either via calling commit or rollback.
Closing the JDBC statement as soon as possible
Therefore, to close the JDBC CallableStatement as soon as possible, you should call release after fetching all the data that you wanted from the stored procedure:
StoredProcedureQuery query = entityManager
.createStoredProcedureQuery("count_comments")
.registerStoredProcedureParameter(
"postId",
Long.class,
ParameterMode.IN
)
.registerStoredProcedureParameter(
"commentCount",
Long.class,
ParameterMode.OUT
)
.setParameter("postId", 1L);
try {
query.execute();
Long commentCount = (Long) query
.getOutputParameterValue("commentCount");
assertEquals(Long.valueOf(2), commentCount);
} finally {
query.unwrap(ProcedureOutputs.class).release();
}
Calling the release method on the associated ProcedureOutputs object in the finally block ensures that the JDBC CallableStatement is closed no matter the outcome of the stored procedure call.
Hibernate 6 onwards
Now, calling release manually is a little bit tedious, so I decided to create the HHH-13215 Jira issue which, from Hibernate ORM 6 onwards, allows you to rewrite the previous example like this:
Long commentCount = doInJPA(entityManager -> {
try(ProcedureCall query = entityManager
.createStoredProcedureQuery("count_comments")
.unwrap(ProcedureCall.class)) {
return (Long) query
.registerStoredProcedureParameter(
"postId",
Long.class,
ParameterMode.IN
)
.registerStoredProcedureParameter(
"commentCount",
Long.class,
ParameterMode.OUT
)
.setParameter("postId", 1L)
.getOutputParameterValue("commentCount");
}
});
Eventually I found the solution, I replaced the line
qr.execute();
with
qr.executeUpdate();
According documetation: "When executeUpdate is called on a StoredProcedureQuery object, the provider will call execute on an unexecuted stored procedure query followed by getUpdateCount. The results of executeUpdate will be those of getUpdateCount"
But they didn't say anything conserning closing cursors but my method works well now.
Here is my solution:
int retValue = -1;
ProcedureCall query = null;
try {
query = (ProcedureCall)entityManager.createNamedStoredProcedureQuery("MyStoredProcedure");
query
.setParameter("param1", value1)
.setParameter("param2", value2)
.execute();
retValue = (int)query.getOutputParameterValue("outParam");
} finally {
query.getOutputs().release();
}

How to call ms sql store procedure using hibernate 4.0

i am working with store procedure
i.e
CREATE PROCEDURE test
(
#INPUTPARAM INT,
#OUTPUTPARAM VARCHAR(20)
)
AS
SELECT #OUTPUTPARAM=S.NAME+','+D.NAME
FROM STUDENT S,DEPARTMENT D
WHERE S.DEPTID=D.DEPARTID AND D.DEPARTID=#INPUTPARAM
BEGIN
END
how to get out parameter from java class using hibernate
please share code example
CREATE PROCEDURE test
(
#INPUTPARAM INT,
#OUTPUTPARAM VARCHAR(20) OUTPUT --<-- You need to use key word "OUTPUT" here
)
AS
BEGIN
SELECT #OUTPUTPARAM = S.NAME + ',' + D.NAME
FROM STUDENT S INNER JOIN DEPARTMENT D
ON S.DEPTID = D.DEPARTID --<-- Use New Syntax of join with On Clause
WHERE D.DEPARTID = #INPUTPARAM
END
EXECUTE Procedure
DECLARE #Var VARCHAR(20);
EXECUTE dbo.test
#INPUTPARAM = 1
#OUTPUTPARAM = #Var OUTPUT --<-- use OUTPUT key word here as well
SELECT #Var
The only way to do it is using em.createNativeQuery and talk directly with you DB Server in SQL.
Update:
Here is, how it could be done:
//get connection from em
Session session = (Session)em.getDelegate();
Connection conn = session.connection();
//Native SQL
final CallableStatement callStmt = conn.prepareCall("{call your.function(?)}");
callStmt.setLong(1, documentId);
callStmt.execute();
if (callStmt.getMoreResults()) {
ResultSet resSet = cStmt.getResultSet();
//Do something good with you result
resSet.close();
}
callStmt.close();
//Don't know if calling conn.close(); is a good idea. Since the session owns it.
Hope that helps a little.
Notes:
If you are using JPA 2.0, you can get the session using
Connection conn = em.unwrap(Session.class).connection();
If you are using JPA 2.1, you can call stored procedures directly
StoredProcedureQuery query = em.createNamedStoredProcedureQuery("ReadAddressById");
query.setParameter("P_ADDRESS_ID", 12345);
List<Address> result = query.getResultList();

Retrieving a value from Stored Procedure using Native SQL Hibernate

Below is the stored procedure:
create or replace procedure
proc_emp_name(v_emp out emp.emp_name%TYPE, v_empid in emp.emp_id%TYPE)
is
begin
select emp_name into v_emp from emp where emp_id = v_empid;
dbms_output.put_line('Emp Name: ' || v_emp);
dbms_output.put_line('Procedure created successfully!!!');
end;
I want to invoke this using Native SQL, followed this link but not sure how to retrieve the OUT parameter from the Procedure.
http://www.mkyong.com/hibernate/how-to-call-store-procedure-in-hibernate/
Kindly let me know the simplest way to invoke the procedure and get the results out of it.
EDIT
As suggested, checking the docs, I modified the Proc having first parameter as a SYS_REFCURSOR as follows:
create or replace procedure
proc_empname_refcursor(v_empname OUT SYS_REFCURSOR, v_deptid in emp.emp_id%type)
is
begin
open v_empname for select * from dept where dept_id = v_deptid;
end;
I am able to invoke it using NamedQuery but I don't want to add anything in the mapping files because of some other restrictions. I tried the below code for invoking the proc without using NamedQuery but it did not worked out:
Query query = session.createSQLQuery(
"CALL proc_empname_refcursor(?, :deptId)")
.addEntity(Dept.class)
.setParameter("deptId", new Integer(2));
List<Dept> departments = query.list();
for(int i=0; i<departments.size(); i++){
Dept department = (Dept)departments.get(i);
System.out.println("Dept Id: " + department.getDeptId());
System.out.println("Dept Name: " + department.getDeptName());
}
I am getting the exception:
org.hibernate.QueryException: Expected positional parameter count: 1, actual parameters: [] [CALL proc_empname_refcursor(?, :deptId)]
at org.hibernate.impl.AbstractQueryImpl.verifyParameters(AbstractQueryImpl.java:319)
at org.hibernate.impl.SQLQueryImpl.verifyParameters(SQLQueryImpl.java:201)
at org.hibernate.impl.SQLQueryImpl.list(SQLQueryImpl.java:145)
at com.jdbc.HibernateStartup.main(HibernateStartup.java:70)
Kindly let me know how to resolve this.
I've managed to get an out parameter from a stored procedure using the following code in Hibernate and MS SQL Server:
#Override
public Serializable generate(SessionImplementor session, Object object) throws HibernateException {
Connection connection = session.connection();
CallableStatement callable = null;
try {
callable = connection.prepareCall("execute [procedure] ?");
callable.registerOutParameter(1, Types.INTEGER);
callable.execute();
int id = callable.getInt(1);
return id;
} catch (SQLException e) {
(...)
} finally {
(...)
}
}
From Hibernate Docs:
You cannot use stored procedures with Hibernate unless you follow some procedure/function rules.
For Oracle, A function must return a result set. The first parameter of a procedure must be an OUT that returns a result set.

How to call a MySQL stored procedure from Hibernate

I am trying to call a MySQL stored procedure from Java Application which uses MySQL. Below is the part in DAO where i used to call a stored procedure 'insertComm'
String opt="REFUND";
Query query = this.getSession().createSQLQuery("CALL insertComm (:remitNo, :opt)")
.setParameter("remitNo", remitNo)
.setParameter("opt", opt);
opt=query.toString();
hemappLogger.info(opt);
But as i query the database and check, the stored procedure hasn't been executed. The 'opt' value is shown as
SQLQueryImpl(CALL insertComm (:remitNo, :opt))
The parameter is okay and application is not showing error also. I can't see what i missed.
Considering you have a simple stored procedure that outputs a basic type:
CREATE PROCEDURE count_comments (
IN postId INT,
OUT commentCount INT
)
BEGIN
SELECT COUNT(*) INTO commentCount
FROM post_comment
WHERE post_comment.post_id = postId;
END
You can call this stored procedure using a JPA StoredProcedureQuery:
StoredProcedureQuery query = entityManager
.createStoredProcedureQuery("count_comments")
.registerStoredProcedureParameter(
"postId", Long.class, ParameterMode.IN)
.registerStoredProcedureParameter(
"commentCount", Long.class, ParameterMode.OUT)
.setParameter("postId", 1L);
query.execute();
Long commentCount = (Long) query
.getOutputParameterValue("commentCount");
If your stored procedure returns a REFCURSOR or a TABLE result:
CREATE PROCEDURE post_comments(IN postId INT)
BEGIN
SELECT *
FROM post_comment
WHERE post_id = postId;
END
You need to call the stored procedure as follows:
StoredProcedureQuery query = entityManager
.createStoredProcedureQuery("post_comments");
query.registerStoredProcedureParameter(1, Long.class, ParameterMode.IN);
query.setParameter(1, 1L);
List<Object[]> postComments = query.getResultList();
For database functions, that return the result set instead of placing it in an OUT variable:
CREATE FUNCTION fn_count_comments(postId integer)
RETURNS integer
DETERMINISTIC
READS SQL DATA
BEGIN
DECLARE commentCount integer;
SELECT COUNT(*) INTO commentCount
FROM post_comment
WHERE post_comment.post_id = postId;
RETURN commentCount;
END
The Hibernate 4.x and 5.x API will not help you, and you have to use JDBC instead:
int 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);
}
});
Unfortunately you can't call a Stored Procedure using Session.createSQLQuery(). As the name suggests it allows to create a SQL Query. A procedure call is not a query.
But fear not, the work around is this.
Connection conn = getSession().connection();
CallableStatment stat = conn.prepareCall("{CALL insertComm (?,?)}");
stat.setString(1, remitNo); // Assuming both parameters are String
stat.setString(2, opt);
stat.executeUpdate();
stat.close();
You didn't add Entity to your session object... .addEntity(classname.class).setParameter()

Categories

Resources