Below is my Oracle stored procedure,
create or replace
PROCEDURE "REPORT_HIBERNATE"(
start_year IN NUMBER,
output_data OUT SYS_REFCURSOR
)
AS
BEGIN
DECLARE
select_query LONG;
BEGIN
select_query :='SELECT
GLOBAL_ID,
PROJECT_NUMBER,
FISCAL_YEAR,
FISCAL_MONTH,
WEEK_END_DATE,
ACTIVITY,
COST_CENTER,
ACTUAL_HOURS,
APPROVED_HOURS,
NORMALIZED_HOURS
FROM TS_TBTIMECARD_WEEKLY_DATA
where FISCAL_YEAR in
(SELECT FISCAL_YEAR
FROM TS_TBTIMECARD_WEEKLY_DATA
where FISCAL_YEAR = ' ||start_year|| '
)';
OPEN output_data FOR select_query;
END;
END REPORT_HIBERNATE;
Below is the Entity class:
#Entity
#SequenceGenerator(name="wkseq", initialValue=1, allocationSize=1)
#Table(name = "TS_TBTIMECARD_WEEKLY_DATA")
#NamedNativeQuery(name = "call_REPORT_HIBERNATE_procedure",
query = "{ CALL REPORT_HIBERNATE(:start_year) }",
resultClass = TimecardWeeklyData.class, hints = {
#javax.persistence.QueryHint(name = "org.hibernate.callable", value = "true") })
public class TimecardWeeklyData {
...
}
DAOImpl:
Query query = sessionFactory.getCurrentSession().getNamedQuery("call_REPORT_HIBERNATE_procedure");
query.setParameter("start_year",2014);
List result = query.list();
I get the following exception:
Hibernate: { CALL REPORT_HIBERNATE(?) }
org.hibernate.exception.GenericJDBCException: could not execute query ...
and
Caused by: java.sql.SQLException: Invalid column index
Plz let me know how will I call this stored procedure using Hibernate Annotation in Spring??
Considering you have 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 the stored procedure 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();
Your OUT parameter isn't first. If you are able to modify your procedure, rearrange the parameters to make the OUT parameter the first parameter, then account for it in your #NamedNativeQuery annotation.
query = "{ CALL REPORT_HIBERNATE(?, :start_year) }",
Related
I am getting an error : Attempt to set a parameter name that does not occur in the SQL: i_reorg_id . It does not make sense to me as there is i_reorg_id in the SQL.
Procedure is:
create or replace PROCEDURE PRINTREORGID(i_reorg_id IN VARCHAR2, o_reorg_id OUT VARCHAR2)
AS BEGIN
SELECT reorg_id
INTO o_reorg_id
FROM reorg_automation_workflowinput
WHERE reorg_id = i_reorg_id;
END;
Entity is:
#Entity
#NamedStoredProcedureQueries({
#NamedStoredProcedureQuery(name = "fetchProcedure", procedureName = "PRINTREORGID", parameters = {
#StoredProcedureParameter(mode = ParameterMode.IN, type = String.class, name = "i_reorg_id"),
#StoredProcedureParameter(type = String.class, mode = ParameterMode.OUT, name = "o_reorg_id"),
})
})
And Repository is:
#Procedure(name = "fetchProcedure", procedureName="PRINTREORGID")
String reorgAutomationWorkFlow(#Param("i_reorg_id") String i_reorg_id);
You can call the procedure another way(This is Best Practice):-
Procedure:-
procedure getEmployeeById(
id_in IN EMPLOYEE.ID%type,
e_disp OUT SYS_REFCURSOR
) IS
hasEmployee number;
BEGIN
hasEmployee := 0;
SELECT count(*) into hasEmployee from EMPLOYEE where ID = id_in;
IF hasEmployee <> 0 THEN --here <> means !=
OPEN e_disp FOR SELECT * FROM EMPLOYEE WHERE ID = id_in;
ELSE
--return empty SYS_REFCURSOR couse 1=2 not equal always
OPEN e_disp FOR SELECT * FROM EMPLOYEE WHERE 1=2;
END IF;
END getEmployeeById;
Call From Java:-
Click here
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.
I have a JPA entity MyEntity which includes a composite primary key in a #Embeddable class MyEntityPK.
I am using a native sql query in method getThreeColumnsFromMyEntity():
public List<MyEntity> getThreeColumnsFromMyEntity() {
List<MyEntity> results = em.createNativeQuery("select pid,name,dateofbirth from (select pid,name, dateofbirth,max(dateofbirth) "
+ "over(partition by pid) latest_dateofbirth from my_entity_table) where"
+ " dateofbirth = latest_dateofbirth;","myEntityMapping").getResultList();
return results;
My #SqlResultSetMapping:
#SqlResultSetMapping(
name = "myEntityMapping",
entities = {
#EntityResult(
entityClass = MyEntityPK.class,
fields = {
#FieldResult(name = "PID", column = "pid"),
#FieldResult(name = "NAME", column = "name")}),
#EntityResult(
entityClass = MyEntity.class,
fields = {
#FieldResult(name = "dateofbirth", column = "dateofbirth")})})
My JPA columns are named : #Column(name="DATEOFBIRTH"), "PID" and "NAME".
I tested my sql statement straight on the db and it works fine.
When i run it on Eclipse I get an Oracle error:
ORA-00911 and "Error code 911 , Query: ResultSetMappingQuery [..]
My guess is there is something wrong with the mapping but I cannot find out what it is.
I assume you get this error because you are missing the alias name for the subquery, so instead you can try this :
select
pid,
name,
dateofbirth
from
(
select
pid,
name,
dateofbirth,
max(dateofbirth) over(partition by pid) AS latest_dateofbirth
from
my_entity_table
) second_result
-- ^--------------- use an aliase name to the subquery
where
second_result.dateofbirth = latest_dateofbirth
-- ^----use the aliase name to reference to any of its fields, in your case 'dateofbirth'
Take a look about the error meaning here ORA-00911: invalid character tips
Procedure
CREATE OR REPLACE PROCEDURE GET_ALL_ITEM_TYPE
(
cur_OUT OUT SYS_REFCURSOR
)
AS
BEGIN
OPEN cur_OUT FOR
SELECT * FROM PG_ITEM_TYPE ;
END GET_ALL_ITEM_TYPE;
Model Class
#NamedNativeQueries({
#NamedNativeQuery(
name = "getAllItemType",
query = "CALL GET_ALL_ITEM_TYPE(?)",
resultClass = GdItemType.class
)
})
DAO
#Override
public List<GdItemType> getAllGdItemType() {
Session session = this.sessionFactory.getCurrentSession();
Query query = session.getNamedQuery("getAllItemType");
List<GdItemType> result = query.list();
return result;
}
I am getting error Expected positional parameter count: 1, actual parameters: [] [CALL GET_ALL_ITEM_TYPE(?)].
I can't understand whats the problem.
Your stored procedure has an output parameter, and the mapping contains a reference to it. As a result, Hibernate expects the parameter to be supplied.
You could try adding a Hibernate query hint
#javax.persistence.QueryHint(name = "org.hibernate.callable", value = "true") }
or changing the stored procedure mapping to
#NamedNativeQueries({
#NamedNativeQuery(
name = "getAllItemType",
query = "? = CALL GET_ALL_ITEM_TYPE()",
resultClass = GdItemType.class
)
})
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()