How to set query parameters with single quotes - java

I'm using a oracle database.I need to run a update query through jpa repository.This is the query I have tried to execute.
#Transactional(propagation = Propagation.REQUIRES_NEW)
#Modifying
#Query(
value = "UPDATE transactionlog SET transactionstatus= :ps,startedat = CURRENT_TIMESTAMP, readytoprocessat= (CURRENT_TIMESTAMP+ interval ':to' second) WHERE logid IN (:li) ",
nativeQuery = true)
public Integer reserve(#Param("ps") short processingStatus, #Param("li") List<Integer> logIdList, #Param("to") int timeOut);
But this exception
org.springframework.dao.InvalidDataAccessApiUsageException: Parameter with that name [to] did not exist; nested exception is java.lang.IllegalArgumentException: Parameter with that name [to] did not exist
But if i change this method as follows, it works fine.
#Transactional(propagation = Propagation.REQUIRES_NEW)
#Modifying
#Query(
value = "UPDATE transactionlog SET transactionstatus= :ps,startedat = CURRENT_TIMESTAMP, readytoprocessat= (CURRENT_TIMESTAMP+ interval '5' second) WHERE logid IN (:li) ",
nativeQuery = true)
public Integer reserve(#Param("ps") short processingStatus, #Param("li") List<Integer> logIdList);
Any idea?

Parameter with name [to] doesn't exist because you put :to between single quotes. Use :to instead of ':to'.
That being said, this will not work anyway. I faced really similar issue and after some hours finally found a solution which I present in answer here. For some reason, when interval comes into play injection of parameters doesn't work as you would expect.
Considering conclusion from the link above - I believe this should work:
#Transactional(propagation = Propagation.REQUIRES_NEW)
#Modifying
#Query(value = "UPDATE transactionlog SET transactionstatus= :ps,
startedat = CURRENT_TIMESTAMP,
readytoprocessat= (CURRENT_TIMESTAMP + (( :to ) || 'second')\\:\\:interval)
WHERE logid IN (:li) ",nativeQuery = true)
public Integer reserve(#Param("ps") short processingStatus, #Param("li") List<Integer> logIdList, #Param("to") int timeOut);

replace the :ps and all other parameter with ?1, ?2, ... and make the methos parameter match SQL parameter (the order will be important) .

I have found an answer for this problem.
#Transactional(propagation = Propagation.REQUIRES_NEW)
#Modifying
#Query(
value = "UPDATE transactionlog SET transactionstatus= :ps,startedat = CURRENT_TIMESTAMP, readytoprocessat= CURRENT_TIMESTAMP+ NUMTODSINTERVAL( :to, 'SECOND' ) WHERE logid IN (:li) ",
nativeQuery = true)
public Integer reserve(#Param("ps") short processingStatus, #Param("li") List<Integer> logIdList, #Param("to") int timeOut);

readytoprocessat= (CURRENT_TIMESTAMP+ interval ':to' second)
This clause is having issue, try to perform it one line prior, separately. Then you would be able to see the problem yourself.

Related

Spring boot Spring Data JPA postgres jsonb column INSERT/UPDATE

i'm struggling trying to update a record on a postgres table with a jsonb column.
What i'm trying to do with spring boot and JPA is running this statement that works great on postgres:
UPDATE data_report
SET error_details = error_details || '[{"error": "testError","record": {"start":"14:00","end":"20:00","id":"AAAA001","date":"2022-01-31"}}]'::jsonb
WHERE id = 91;
I've tried with Native Query:
#Transactional
#Modifying
#Query(value = "UPDATE data_integration_report SET error_details = error_details || :errors ::jsonb WHERE id = :id", nativeQuery = true)
void updateErrorDetail(#Param("id") Long id, #Param("errors") String errors);
but I'm getting error saying that syntax is not correct because ::jsonb is not recognized
I've tried with EntityManager
entityManager.createNativeQuery(
"UPDATE data_integration_report SET error_details = error_details || :errors ::jsonb WHERE id = :id"
).setParameter("id", 91L)
.setParameter("errors", data)
.executeUpdate();
Even here i'm getting error on syntax.
I've also tried to remove ::jsonb cast, but I'm receiving this error: "column is of type jsonb but expression is of type text"
I'm looking for some documentation or an example that can help me to find a solution.
Thanks in advance.
I think the immediate problem you are having is casting the incoming field incorrectly.
#Transactional
#Modifying
#Query(value = "UPDATE data_integration_report SET error_details = error_details || :errors ::jsonb WHERE id = :id", nativeQuery = true)
void updateErrorDetail(#Param("id") Long id, #Param("errors") String errors);
:errors ::jsonb has a space, so it sees the :: cast operation as a separate token. However, JPA will choke on :errors::jsonb (as I suspect you have discovered).
There are two ways to do a Postgres cast inside a query like this:
Escape the colons: :error\\:\\:jsonb
Use the cast function: cast(:error as jsonb)
However
There is an even better solution, and that is to use a hibernate type made for jsonb in your entity. The commonly accepted solution is this one: vladmihalcea /
hibernate-types

How to use jsonb_set in jpa repository, for passing dynamic strings?

postgres command works in pgadmin4 but not in java code
String toAdd = "case_data->'business' || '{\"l\":\"cpaz\"}'";
this.orchestrateRepo.updateColumn(toAdd, case_id);
#Query(value = "Update onboarding.onboarding_cases set case_data = jsonb_set(case_data, '{business}', ?1 )where case_id=?2", nativeQuery = true)
void updateColumn(String toAdd, BigInteger case_id);
I am passing a string toAdd,i want to insert the value dynamically..but it gives error
org.postgresql.util.PSQLException: ERROR: function jsonb_set(jsonb, unknown, character varying) does not exist
Hint: No function matches the given name and argument types. You might need to add explicit type casts.
the query works fine if i write it like this
#Query(value = "Update onboarding.onboarding_cases set case_data = jsonb_set(case_data, '{business}', case_data->'business' || '{"t":"cpaz"}' )where case_id=?2", nativeQuery = true)
void updateColumn(BigInteger case_id);
What should i do
Finally solved this issue. Posting an answer for those looking.
Only passing key value.
String toAdd = "value";
Changed the query to:
#Query(value = "Update onboarding.onboarding_cases set case_data=jsonb_set(case_data,'{business,key)}',to_jsonb(?1)) where case_id=?2", nativeQuery = true)
void updateColumn(String toAdd, BigInteger case_id);

Handling null values from database in spring jpa

This works fine:
#Repository
public interface VoteDao extends CrudRepository <Vote, Long> {
#Query(value = "select sum(points) from votes where siteuserid= ?1", nativeQuery = true)
int countBySiteUser(#Param("user") SiteUser user);
}
Except in case when there are no votes yet that the result is NULL and the problem is that I do not know how to handle that case of checking when it is Null since the query does no return anything when I ask...
System.out.println("!!!!: PROPOSAL VoteService: " + voteDao.countBySiteUser(user));
Should it print a Null value for that sysout? The DAO is supposed to answer with a NULL value, but it is not. I would be able to handle that NULL if provided, but it does not.
Thanks in advance for your help!
Use COALESCE to handle null as 0, which correspond to what you actually mean.
#Query(
value = "SELECT COALESCE(SUM(points), 0) FROM votes WHERE siteuserid = ?1",
nativeQuery = true)
int countBySiteUser(#Param("user") SiteUser user);
... or another solution with a programmatic approach:
// Integer instead of int to add the "null" handling
#Query(
value = "SELECT SUM(points) FROM votes WHERE siteuserid = ?1",
nativeQuery = true)
Integer countBySiteUser(#Param("user") SiteUser user);
Usage:
Integer count = voteDao.countBySiteUser(user);
if (count == null) {
count = 0;
}
System.out.println("!!!!: PROPOSAL VoteService: " + count);
The COALESCE solution seems better to me. But as #EJP said, it will depend on your needs.

org.postgresql.util.PSQLException: No results were returned by the query [duplicate]

I am trying to insert a data into a table. After executing the query i am getting an exception stating
org.postgresql.util.PSQLException: No results were returned by the query.
org.postgresql.jdbc2.AbstractJdbc2Statement.executeQuery(AbstractJdbc2Statement.java:284)
The data is getting inserted successfully, but i have no idea why i am getting this exception ??
Use
executeUpdate
instead of
executeQuery
if no data will be returned (i.e. a non-SELECT operation).
Please use #Modifying annotation over the #Query annotation.
#Modifying
#Query(value = "UPDATE Users set coins_balance = coins_balance + :coinsToAddOrRemove where user_id = :user_id", nativeQuery = true)
int updateCoinsBalance(#Param("user_id") Long userId, #Param("coinsToAddOrRemove") Integer coinsToAddOrRemove);
The same is true for any DML query (i.e. DELETE, UPDATE or INSERT)
Using #Modifying and #Transaction fixed me
The problem that brought me to this question was a bit different - I was getting this error when deleting rows using an interface-based Spring JPA Repository. The cause was that my method signature was supposed to return some results:
#Modifying
#Query(value = "DELETE FROM table t WHERE t.some_id IN (:someIds)", nativeQuery = true)
List<Long> deleteBySomeIdIn(#Param("someIds") Collection<Long> someIds);
Changing the return type to void resolved the issue:
#Modifying
#Query(value = "DELETE FROM table t WHERE t.some_id IN (:someIds)", nativeQuery = true)
void deleteBySomeIdIn(#Param("someIds") Collection<Long> someIds);
If you want last generated id, you can use this code after using executeUpdate() method
int update = statement.executeUpdate()
ResultSet rs = statement.getGeneratedKeys();
if (rs != null && rs.next()) {
key = rs.getLong(1);
}
I have solved this Problem using addBatch and executeBatch as following:
statement.addBatch("DELETE FROM public.session_event WHERE id = " + entry.getKey());
statement.executeBatch();

Return a boolean from a JpaRepository method

I have a native query in an interface which extends JpaRepository. The method should ideally return a boolean value, but I can't figure out how to SELECT anything that gets automatically translated into boolean.
This works, although I have to call it as Boolean.valueOf(hasKids(id)):
// yuck. I wanted a boolean
#Query(nativeQuery = true, value = "select 'true' from dual where exists("
+ "select * from child_table where parent_id = ?)")
String hasKids(long parentId);
How can I change this to the more natural return type?
boolean hasKids(long parentId); // throws ClassCastException
Update:
the stacktrace is not very helpful IMHO because it's the usual nightmare of Hibernate proxies and AspectJ closures, but here's the relevant portion anyway.
Caused by: java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Boolean
at com.sun.proxy.$Proxy1025.hasKids(Unknown Source)
at com.bela.foo.bar.Service.ThingyServiceImpl.recordHasKids_aroundBody4(ThingyServiceImpl.java:85)
at com.bela.foo.bar.Service.ThingyServiceImpl$AjcClosure5.run(ThingyServiceImpl.java:1)
...
I ran into a similar problem. My solution was to use a projection of java.lang.Boolean.
#Query("select new java.lang.Boolean(count(*) > 0) from child_table where parent_id = ?")
Boolean hasKids(long parentId);
Hope this helps someone.
I think you want to check the row exist or not for the parent id,and return true and false on the basis of that, then go for the case.
Changes to made in query
"select case when (count(*) >0) then true else false end from dual where exists("
+ "select * from child_table where parent_id = ?)
I tested this by removing the single quotes around true and it works.
#Query(nativeQuery = true, value = "select true from dual where exists("
+ "select * from child_table where parent_id = ?)")
String hasKids(long parentId);
With my Oracle constraint and a combination of all the suggestions here, I found a solution that worked for my situation without having to call Boolean.valueOf(hasKids(id)):
#Query(nativeQuery = true, value = "select case when exists(select * from child_table "
+ "where parent_id = :parentId) then 'true' else 'false' end from dual")
Boolean hasKids(#Param("parentId") long parentId);
There seems to be an issue, at least with mysql
(count() >0) convert to boolean fine when the query is non native
(count() >0) returns a "BigInteger cannot be cast to java.lang.Boolean" when the query is native

Categories