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

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)

Related

Is there way to set default as null for SQL parameter?

I have a code that creates sql parameters using MapSqlParameterSource. Here is my code:
MapSqlParameterSource parameters = new MapSqlParameterSource()
.addValue(EVENT_ID, eventId)
.addValue(TYPE, type.toString())
.addValue(ACCOUNT_ID, null)
.addValue(USER_ID, null);
if (Type.SPOOFER_USER == type) {
parameters.addValue(USER_ID, account.getUser().getId());
}
else {
parameters.addValue(ACCOUNT_ID, account.getId());
}
Basically, if account type is spoofer, I have to have user id instead of account id. However, I don't like that I have to set account_id and user_id to null when I instantiate parameters. Is there way to set account_id and user_id as null so I don't have to write this two lines?:
MapSqlParameterSource parameters = new MapSqlParameterSource()
.addValue(EVENT_ID, eventId)
.addValue(TYPE, type.toString())
.addValue(ACCOUNT_ID, null) //////////////////////////THIS ONE
.addValue(USER_ID, null); //////////////////////////AND THIS ONE
Here is my sql query:
INSERT INTO database (id, event_id, type, account_id, user_id)
VALUES (database.nextval, :event_id, :type, :account_id, :user_id)
Update:
Maybe my question was not specific enough. What happens is that when I run
jdbcTemplate.update(insertEventExtra, parameters);
With the given parameters without making them "NULL", I get this exception in my unit test:
org.springframework.dao.InvalidDataAccessApiUsageException: No value supplied for the SQL parameter 'user_id': No value registered for key 'user_id'
I use hsql to test it. my .sql looks like this:
...
ID NUMBER(38,0) PRIMARY KEY,
EVENT_ID BIGINT NOT NULL,
TYPE VARCHAR2(20 BYTE) NOT NULL,
ACCOUNT_ID NUMBER(38,0),
GROUP_ID NUMBER(38,0),
USER_ID NUMBER(38,0),
...
So my specific question is that my test is giving me exception when I try to run test with parameters without setting them to null.
You must include the addValue(ACCOUNT_ID, null) and addValue(USER_ID, null) because your INSERT statement includes the two named parameters :account_id, :user_id.
The framework attempts to extract the values for the named parameters from the MapSqlParameterSource object and when it does not find one of them, it throws the exception. It does this to avoid user errors, because if you didn't intend to provide a value for a parameter, you wouldn't include the parameter in the INSERT statement.
Make your columns nullable and default to null in the database schema. Then, if you don't specify a value for a column when inserting, it should default to null
By default all not null columns have default value of NULL unless and until you provide value while inserting or updating the column.

Is it possible to add custom mapper for Hibernate from NUMBER database type to Java Integer?

There is a problem with mapping from Snowflake NUMBER type to Java Integer because it is not possible to set INTEGER-like database type because Snowflake converts it to NUMBER(38, 0).
You can read about it here: https://docs.snowflake.com/en/sql-reference/data-types-numeric.html#int-integer-bigint-smallint-tinyint-byteint
So I would like to ask is it possible to make JPA returning Integer type instead of BigInteger?
Here is table ddl:
CREATE TABLE person
(
name TEXT,
age INTEGER -- will be converted to NUMBER(38, 0) automatically
);
And here is JPA Repository method:
#Query(value = "SELECT age FROM person WHERE name = :name", nativeQuery = true)
Set<Integer> findAgesByName(#Param("name") String name);
As a result findAgesByName return Set<BigInteger> instead of Set<Integer> and I receive ClassCastException.
Thanks in advance!
That is correct it is converted to Numeric(38,0) to help with performance and storage.
Documentation reference: here
Could the table methods listed here be helpful?
JDBC API Methods: https://docs.snowflake.com/en/user-guide/jdbc-api.html#methods
Adding a getInt() to your query?

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.

LocalDateTime turning into bytea with a StoredProcedureQuery

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.

Table-valued parameters into stored procedure using Hibernate

I'm trying to use a table-valued parameter for a stored procedure we're calling using Hibernate's Session.createSQLQuery.
I have created a type and stored procedure in SQL:
CREATE TYPE StringListType AS TABLE
(
StringText NVARCHAR(256)
)
CREATE PROCEDURE [dbo].[TestStringListType]
(
#stringList StringListType READONLY
)
AS
SELECT * FROM #stringList
I can use this in SQL with:
BEGIN
Declare #StringListTemp As StringListType
insert INTO #StringListTemp (StringText)
values ('foo'), ('bar'), ('baz')
EXEC TestStringListType #StringListTemp
END
What I would like to do in Java is something like:
String fakeQueryStr = "call TestStringListType :list";
SQLQuery fakeQuery = getSession().createSQLQuery(fakeQueryStr);
ArrayList<String> data = Lists.newArrayList("foo", "bar", "baz");
fakeQuery.setParameter("list", data);
return fakeQuery.list();
Neither setParameter or setParameterList work here of course. How do I map my list of Strings to this type to use as a parameter?
I was unable to find a solution for this problem as written. My work-around was to copy the entirety of the stored procedure into a string in Java. In the above example, that would mean that I replaced:
String fakeQueryStr = "call TestStringListType :list";
with
String fakeQueryStr = "SELECT * FROM :list";
In my actual code, this was undesirable, because the stored procedure was a significantly longer set of statements, but it does still work when wrapped in BEGIN and END within the string.

Categories

Resources