LocalDateTime turning into bytea with a StoredProcedureQuery - java

Using Java 8, Spring 4.3.9 and a Postgre database.
I have a stored function defined as followed :
CREATE OR REPLACE FUNCTION public.getinformations (
IN stateId integer,
IN dateStart timestamp,
IN dateEnd timestamp
)
RETURNS SETOF DECLAREDTYPE
LANGUAGE plpgsql
AS $$ [...]
I want to call this in Spring and used StoredProcedureQuery :
StoredProcedureQuery query = entityManager.createStoredProcedureQuery("public.getinformations ")
.registerStoredProcedureParameter("stateId",Long.class,ParameterMode.IN)
.registerStoredProcedureParameter("dateStart",LocalDateTime.class,ParameterMode.IN)
.registerStoredProcedureParameter("dateEnd",LocalDateTime.class,ParameterMode.IN);
But when the function is fetched I'm getting the following error :
ERROR: function public.getinformations (bigint, bytea, bytea) does not exist
Hint: No function matches the given name and argument types. You might need to add explicit type casts.

Related

How to pass null values to call procedure using native query?

I call a stored procedure using JPA native query and PostgreSQL DB.
#Repository:
#Modifying
#Query(value="call proc(?1,?2,?3,?4,?5)",nativeQuery = true)
void saveProc(#Param("reply_by") Long replyBy,
#Param("reply_to") Long replyTo,
#Param("reply") String reply,
#Param("status"),
#Param("app_id") long appId);
As some of the parameter are null (foreign key).
ServiceImpl:
repo.saveProc(2,null,"dvvf",null,null) // java null
I'm getting this error:
procedure proc(bigint, bytea, character varying, bytea,bytea) does not exist
Hint: No procedure matches the given name and argument types. You might need to add explicit type casts.
How can I pass null values instead of bytea?
You have to cast the parameter that could be null because Hibernate does not know the parameter type: #Query(value="call proc(cast(?1 as bigint),cast(?2 as bigint),?3,cast(?4 as bigint),cast(?5 as bigint))",nativeQuery = true)

How to let ignite's cast function return null instead of throwing an exception?

When I use ignate's cast function, when the source data cannot be converted to target type, then ignite will throw this exception:
javax.cache.CacheException: Failed to run map query remotely.Failed to execute map query on the node: 3ed8c3f3-31d3-4e18-a41c-83e2b6d4bf4a, class org.apache.ignite.IgniteCheckedException:Failed to execute SQL query. Cannot parse "DATE" constant "xxxx";
But I need it to use null instead when it cannot be converted, just like the Postgresql database.
What should I do?
Ignite version:2.6,JDBC version:2.5
SELECT CAST(city AS DATE) AS `a1` FROM orders GROUP BY CAST(city AS DATE);
Expected:
a1
Null
Actual:
java.sql.SQLException: javax.cache.CacheException: Failed to run map query remotely.Failed to execute map query on the node: 3ed8c3f3-31d3-4e18-a41c-83e2b6d4bf4a, class org.apache.ignite.IgniteCheckedException:Failed to execute SQL query. Cannot parse "DATE" constant "xxxx"; SQL statement:
SELECT
CAST(__Z0.city AS DATE) __C0_0
FROM PUBLIC.ORDERS2 __Z0
GROUP BY CAST(__Z0.city AS DATE) LIMIT 3 [22007-195]
at org.apache.ignite.internal.jdbc.thin.JdbcThinConnection.sendRequest(JdbcThinConnection.java:751)
at org.apache.ignite.internal.jdbc.thin.JdbcThinStatement.execute0(JdbcThinStatement.java:210)
at org.apache.ignite.internal.jdbc.thin.JdbcThinPreparedStatement.executeWithArguments(JdbcThinPreparedStatement.java:252)
at org.apache.ignite.internal.jdbc.thin.JdbcThinPreparedStatement.executeQuery(JdbcThinPreparedStatement.java:78)
at com.uniplore.calculation.connectors.IgniteConnector.execute(IgniteConnector.java:58)
at tests.AbstractFunctionTransTest.getResult(AbstractFunctionTransTest.java:65)
at tests.IgniteFunctionTransTest.Date(IgniteFunctionTransTest.java:840)
Is that how PostgreSQL's CAST work? This post kind of suggests otherwise.
In any case, you can't change this behavior in Ignite. If you use the same value xxxx as a placeholder date, you can use CASE as a workaround:
SELECT CASE WHEN city = 'xxxx' THEN NULL ELSE CAST(city AS DATE) END AS a1
FROM orders GROUP BY a1
Ok,I know,CAST is a standard function that should return exceptions。
Mysql has a function called DATE that can return NULL values when the conversion fails,except ignite has no corresponding function.
The best way is to return the error to the user, or let the user use the PARSEDATETIME function instead.

jOOQ fetched timestamp has no nanoseconds

In a postgres db, using jOOQ, when I fetch a row that has a column defined as
timestamp without time zone
when I do a select and I get the value (fetched by jOOQ into a java.sql.Timestamp), then I see that the nanoseconds are missing.
E.g., in the database I have:
2016-04-04 15:14:10.970048
but jOOQ returns a Timestamp with value
2016-04-04 15:14:10.0
This is a problem for further comparisons. How can I prevent this?
UPDATE
Upon request, I'll provide more details.
In Postgresql I have a type:
CREATE TYPE schema.my_type AS
(
mt_page smallint,
mt_active_from timestamp without time zone,
);
I call a function, using the routines:
DSLContext dsl = ....
MyTypeRecord [] records = Routines.myRoutine(dsl.configuration);
Then, the Timestamp will not have no nanos
The function is:
CREATE OR REPLACE FUNCTION shop.myRoutine(
OUT my_types schema.my_type[]
)
RETURNS schema.my_type[] AS
$BODY$
DECLARE
BEGIN
BEGIN
SELECT ARRAY(
SELECT
ROW(a_id, a_timestamp)::schema.my_type
FROM schema.article
) INTO my_types;
END;
RETURN;
END;
$BODY$
LANGUAGE plpgsql VOLATILE SECURITY DEFINER
COST 1000;
This is a bug in jOOQ: https://github.com/jOOQ/jOOQ/issues/5193
jOOQ internally implements a composite type deserialisation algorithm, as PostgreSQL's JDBC driver, unfortunately, doesn't implement SQLData and related API for out-of-the-box composite type support. The current implementation (jOOQ 3.7.3) parses timestamps without their fractional seconds part.
As a workaround, you could implement your own data type binding to work around this issue.

JPA 2.1 StoredProcedureQuery with PostgreSQL and REF_CURSORs

I have a function I created in my PostgreSQL DB that I want to call using JPA 2.1's StoredProcedureQuery method.
Here is my PostgreSQL query:
CREATE OR REPLACE FUNCTION get_values(date text) returns refcursor
AS $$
DECLARE tuples refcursor;
BEGIN OPEN tuples FOR
SELECT user, COUNT(*)
FROM my_table
WHERE date_ = date
GROUP BY user;
return tuples;
END;
$$
LANGUAGE plpgsql
This is just a simple query to count users on a particular day. This is just a demo query to test how the StoredProcedureQueries work. And in fact, it works just fine when used via postgreSQL alone.
Now, let's try and call this using JPA 2.1 and in Javaland:
StoredProcedureQuery storedProcedure = em.createStoredProcedureQuery("get_values");
storedProcedure.registerStoredProcedureParameter(2, String.class, ParameterMode.IN);
storedProcedure.registerStoredProcedureParameter(1, Object.class, ParameterMode.REF_CURSOR);
storedProcedure.setParameter(2, "2015-02-01");
storedProcedure.execute();
When I do this, I get back the following exception:
org.hibernate.HibernateException: PostgreSQL supports only one REF_CURSOR parameter, but multiple were registered
There is only a single ref cursor declared! In fact, if I just register the single REF_CURSOR parameter and hardcode in a value for my Postgresql function for the WHERE date_ = date, this call works just fine.
So it would seem adding any additional parameters to a storedprocedurequery with a ref_cursor breaks the functionality. Alone, the ref_cursor parameters works fine.
Anybody see why this would happen?? Why is it that adding parameters to the StoredProcedureQuery for my PostgreSQL function breaks it?
Example of when it works:
CREATE OR REPLACE FUNCTION get_values(date text) returns refcursor
AS $$
DECLARE tuples refcursor;
BEGIN OPEN tuples FOR
SELECT user, COUNT(*)
FROM my_table
WHERE date_ = '2015-02-01'
GROUP BY user;
return tuples;
END;
$$
LANGUAGE plpgsql
and in javaland:
StoredProcedureQuery storedProcedure = em.createStoredProcedureQuery("get_values");
storedProcedure.registerStoredProcedureParameter(1, Object.class, ParameterMode.REF_CURSOR);
storedProcedure.execute();
Short answer: Reverse the order of your two calls to registerStoredProcedureParameter():
storedProcedure.registerStoredProcedureParameter(1, Object.class, ParameterMode.REF_CURSOR);
storedProcedure.registerStoredProcedureParameter(2, String.class, ParameterMode.IN);
Long answer: I did some digging in the Hibernate source code for Postgress callable statement support, and found that each registerStoredProcedureParameter() call creates a ParameterRegistrationImplementor instance that gets tacked into a list and passed around. You'll note that this class stores the position of the parameter, which is independent of its position within the list.
Later, this list is analyzed and assumes that the REF_CURSOR parameter will be first in line, and throws your error message if a REF_CURSOR parameter is not first, regardless of what the parameter number is.
Not a very bright way of doing things (IMHO), but at least the workaround is easy: if you swap the order of your calls, you should be fine.

How to specify IN parameter that as Oracle data type of TABLE type?

Based on this previous question on stackoverflow:
Fetch Oracle table type from stored procedure using JDBC
The answer gave us a sample of OUT parameter using Oracle getArray and java.sql.Datum. But what if I want to specify IN parameter that has data type of Oracle TABLE type?
CREATE OR REPLACE TYPE XXINV.XX_PROD_SRCH_RSLT_REC_TYPE IS OBJECT(
item_no VARCHAR2(30),
inventory_item_id NUMBER,
organization_id NUMBER,
item_description VARCHAR2(240),
item_long_description VARCHAR2(240),
cat_description VARCHAR2(240),
category_set_name VARCHAR2(240),
nla_flag VARCHAR2(1),
CONSTRUCTOR FUNCTION XX_PROD_SRCH_RSLT_REC_TYPE RETURN SELF AS RESULT ,
MEMBER PROCEDURE log_prod_srch_rslt_rec_values (SELF IN XX_PROD_SRCH_RSLT_REC_TYPE) )
CREATE OR REPLACE TYPE xxinv.XX_PROD_SRCH_RSLT_TAB_TYPE AS TABLE OF XX_PROD_SRCH_RSLT_REC_TYPE;
And my SP is this:
XX_PART_RESEARCH_PKG .GET_PARTS
(p_called_from IN VARCHAR2,
p_item_id IN NUMBER,
p_category_id IN NUMBER,
p_mnfg_part_id IN NUMBER,
p_item_desc IN VARCHAR2,
p_include_NLA_items IN VARCHAR2,
p_catl_group_id IN NUMBER,
p_catl_attributes IN XX_PROD_ATTR_TAB_TYPE,
x_srch_rslt IN XX_PROD_SRCH_RSLT_TAB_TYPE,
x_return_status OUT VARCHAR2,
x_returb_msg OUT VARCHAR2
)
where as the XX_PROD_ATTR_TAB_TYPE is a table as IN parameter.
How to specify this in Java? I'm using Oracle 10g release 2.
As your type has a constructor defined, I am not sure if this will work exactly, but I have posted samples on how to do this over on my blog. First, how to pass a record type into Oracle from Java:
http://betteratoracle.com/posts/31-passing-record-types-between-oracle-and-java
And then extended it to pass arrays of records into Oracle from Java:
http://betteratoracle.com/posts/32-passing-arrays-of-record-types-between-oracle-and-java

Categories

Resources