Error while ignoring null query parameters - Spring JPA - postgresql

I am using Spring JPA in my application to fetch certain records from the DB. Now, one of the query parameters that I am passing can be null in certain criteria. So I have designed the query in such a way that if the query parameter is not null, then the query parameter is used for extraction otherwise it is ignored.
Query
#Query(value = "SELECT * FROM fulfilment_acknowledgement WHERE entity_id = :entityId " +
"and item_id = :itemId " +
"and (fulfilment_id is null OR :fulfilmentId is null OR fulfilment_id = :fulfilmentId) " +
"and type = :type", nativeQuery = true)
FulfilmentAcknowledgement findFulfilmentAcknowledgement(#Param(value = "entityId") String entityId, #Param(value = "itemId") String itemId,
#Param(value = "fulfilmentId") Long fulfilmentId, #Param(value = "type") String type);
NOTE: The type of fulfilment_id in the table fulfilment_acknowledgement is int8. It is a Postgres RDS.
Now, if I encounter a scenario where the fulfilmentId is actually blank, I am getting the below error:
2022-06-23 15:31:56,997 89645 [boundedElastic-5] DEBUG org.hibernate.SQL - SELECT * FROM fulfilment_acknowledgement WHERE entity_id = ? and item_id = ? and (fulfilment_id is null OR ? is null OR fulfilment_id = ?) and type = ?
2022-06-23 15:31:57,154 89802 [boundedElastic-5] DEBUG o.h.e.jdbc.spi.SqlExceptionHelper - could not extract ResultSet [n/a]
Exception while creating Fulfilment Acknowledgement: [could not extract ResultSet; SQL [n/a]; nested exception is org.hibernate.exception.SQLGrammarException: could not extract ResultSet]
Caused by: org.hibernate.exception.SQLGrammarException: could not extract ResultSet
Caused by: org.postgresql.util.PSQLException: ERROR: operator does not exist: bigint = bytea
Hint: No operator matches the given name and argument types. You might need to add explicit type casts.
I have a solution in mind where I can update the fulfilmentId to a default value like -1 if it is null, but I need to understand why is it failing? What am I missing here?

I was running into similar issue for a native query, which find records with matching column values. When the value for any parameter is null, hibernate uses the wrong type throws casting exceptions. After spending some time I found that in such cases need to use Hibernate's TypedParameterValue
So in your case #Param(value = "fulfilmentId") Long fulfilmentId would be
#Param(value = "fulfilmentId") TypedParameterValue fulfilmentId
Also from the service you need to convert your parameter accordingly from Long to TypedParameterValue as below:
TypedParameterValue fulfilmentIdParam = new TypedParameterValue(StandardBasicTypes.LONG, fulfilmentId);

Related

Spring data native query filtering by List<String>

Given:
Interface projection model with
String getType();
Postgres 13;
Hibernate+JPA+Spring data;
Query filter:
" where ... "
+ " and (COALESCE(:types) is null or model.type in (:types)) "
Pageable method in repository contains param
#Param("types") #Nullable List<String> types
generated SQL when null types is being passed:
and (COALESCE(?) is null or folderModel.contentType in (?))
error after it thrown:
org.postgresql.util.PSQLException: ERROR: operator does not exist: character varying = bytea
Tips: No operator matches the given name and argument types. You might need to add explicit type casts.
There is no problem when I pass any nullable params in 'where' clause likewise when types isn't null and contains valid types. I tried to cast(model.type to varchar) with no any success. Any ideas?

Spring Data JPA #Query NamedQuery is not working for two #Params

Only one #Param is passed, it is working well like below, either firstName or lastName. spring-boot version 2.2.0.RELEASE is using.
#Query(value = "select s from SalesTransaction s where (:firstName is null or LOWER(s.firstName) LIKE LOWER(concat('%',concat(:firstName,'%'))))")
List<SalesTransaction> findFromFirstName(#Param("firstName") String firstName);
But if pass two #Params it is throwing the below exception. firstName and lastName are of type TEXT in entity definition. If fistName is passing, lastName is setting as 'null' from Controller and vice-versa.
#Query(value = "select s from SalesTransaction s where (:firstName is null or LOWER(s.firstName) LIKE LOWER(concat('%',concat(:firstName,'%')))) and (:lastName is null or LOWER(s.lastName) LIKE LOWER(concat('%',concat(:lastName,'%'))))")
List<SalesTransaction> findFromFirstName(#Param("firstName") String firstName,#Param("lastName") String lastName);
Exception on runtime:
[32m2020-05-11 21:09:17,237[0;39m [31mWARN [0;39m [[34mhttp-nio-8080-exec-5[0;39m] [33morg.hibernate.engine.jdbc.spi.SqlExceptionHelper[0;39m: SQL Error: 0, SQLState: 42883
[32m2020-05-11 21:09:17,237[0;39m [1;31mERROR[0;39m [[34mhttp-nio-8080-exec-5[0;39m] [33morg.hibernate.engine.jdbc.spi.SqlExceptionHelper[0;39m: ERROR: function lower(bytea) does not exist
Hint: No function matches the given name and argument types. You might need to add explicit type casts.
Position: 1324
Only one input #Param is working at a time. Please suggest any way to resolve this issue.
Hibernate Generated SQL Query
salestrans0_.transaction_ref as transac28_1_ from sales_transaction.sales_transaction salestrans0_ where (? is null or lower(salestrans0_.first_name) like lower(('%'||(?||'%')))) and (? is null or lower(salestrans0_.last_name) like lower(('%'||(?||'%'))))

JdbcTemplate query returns BadSqlGrammarException

Postgres db:
CREATE TYPE pr_status_name AS ENUM ('CREATED', 'SUCCESS', 'FAILED');
create table payment_request_statuses(
status_id serial PRIMARY KEY,
pr_status_name pr_status_name NOT NULL
);
INSERT INTO payment_request_statuses(pr_status_name) VALUES ('CREATED');
INSERT INTO payment_request_statuses(pr_status_name) VALUES ('SUCCESS');
INSERT INTO payment_request_statuses(pr_status_name) VALUES ('FAILED');
when I am trying to execute the method:
Map<String, Object> data = jdbcTemplate.queryForMap("select * from payment_request_statuses where pr_status_name = ?", new Object[]{"CREATED"});
I am getting the following error:
rg.springframework.jdbc.BadSqlGrammarException: PreparedStatementCallback; bad SQL grammar [select * from payment_request_statuses where pr_status_name = ?]; nested exception is org.postgresql.util.PSQLException: ERROR: operator does not exist: pr_status_name = character varying
Hint: No operator matches the given name and argument types. You might need to add explicit type casts.
Seems, jdbcTemplate for some reason is not able transform/match String to db Enum object that is surprise for me.
How can I fix it?
You need to cast from String to Enum in SQL.
Map<String, Object> data = jdbcTemplate.queryForMap("select * from payment_request_statuses where pr_status_name = ?::pr_status_name", new Object[]{"CREATED"});

PostgreSQL, Spring Data JPA: Integer null interpreted as bytea

In PostgreSQL I have the table
CREATE TABLE public.my_table
(
id integer NOT NULL,
...
I want to perform the query: Show me the rows with a given id. If id is null, show me all rows.
I tried it with
public interface MyRepository extends JpaRepository<MyTable, Integer> {
#Query(value = "SELECT * FROM my_table WHERE (?1 IS NULL OR id = ?1)", nativeQuery = true)
List<MyTable> findAll(Integer id);
If id != null, everything is fine. But if id == null, I will receive the error
org.springframework.dao.InvalidDataAccessResourceUsageException: could not extract ResultSet; SQL [n/a]; nested exception is org.hibernate.exception.SQLGrammarException: could not extract ResultSet
at org.springframework.orm.jpa.vendor.HibernateJpaDialect.convertHibernateAccessException(HibernateJpaDialect.java:261) ~[spring-orm-4.3.13.RELEASE.jar:4.3.13.RELEASE]
...
Caused by: org.hibernate.exception.SQLGrammarException: could not extract ResultSet
at org.hibernate.exception.internal.SQLStateConversionDelegate.convert(SQLStateConversionDelegate.java:106) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
...
Caused by: org.postgresql.util.PSQLException: ERROR: operator does not exist: integer = bytea
Hint: No operator matches the given name and argument type(s). You might need to add explicit type casts.
at org.postgresql.core.v3.QueryExecutorImpl.receiveErrorResponse(QueryExecutorImpl.java:2440) ~[postgresql-42.2.5.jar:42.2.5]
...
Obviously short circuit evaluation does not work and null is transformed into bytea.
As a workaround I have changed the query value into
SELECT * FROM my_table WHERE (?1 IS NULL OR id = (CAST (CAST(?1 AS character varying) AS integer)))
But this is not nice, because the int is cast to string and to int again. Do you have a better solution, e.g. a better cast or sql query?
Another workaround for this is to create the query manually from the EntityManager (em in the example) and call setParameter on it once with a non-null value, then again with the real value.
private static final Integer exampleInt = 1;
List<MyTable> findAll(Integer id) {
return em.createNativeQuery("SELECT * FROM my_table WHERE (:id IS NULL OR id = :id)", MyTable.class)
.setParameter("id", exampleInt)
.setParameter("id", id)
.resultList();
}
This ensures that Hibernate knows the type of value the next second time it's called, even if it's null.
The fault is in the PostgreSQL server, and not in Hibernate, but they have refused to fix it, because it works as intended. You just have a few hundred types of SQL NULL on the server and they're mostly incompatible with each-other, even though it's supposed to be one singular special value.

Spring JPA Hibernate giving error updating timestamp wo timezone in postgres

I have been trying to update timestamp field using an update stmt. I tried java.sql.timestamp, java.util.date, Calendar, LocalDateTime, ZonedDateTime and bunch of other java date util packages. None of them seem to be working.
Column: commit_ts (TimeStamp without Timezone in Postgresql) is defined in our JPA/Hibernate as
#Column(name = "COMMIT_TS")
#Temporal(TemporalType.TIMESTAMP)
private Timestamp commitTs;
here is the Query
#Timed(name = "updateWorkAllocationStatus")
#Transactional
#Modifying(clearAutomatically = true)
#Query(
nativeQuery = true,
value = "UPDATE wlm_work_allocation SET commit_ts=:ts " +
"WHERE allocation_id = :allocationId " +
"and status = :status " +
"and commit_ts == null"
)
int updateWorkAllocationStatus(
#Param("timestamp") Timestamp ts,
#Param("allocationId")Long allocationId,
#Param("status")String status
);
I also tried NativeQueries
#NamedNativeQuery(name = "WorkAllocationEntity.updateCommitTs",
query="UPDATE wlm_work_allocation SET commit_ts= TIMESTAMP WHERE allocation_id=:allocationId and status=:status and commit_ts==null")
Note: According to this link,The SQL standard requires that writing just timestamp be equivalent to timestamp without time zone, and PostgreSQL honors that behavior.
https://www.postgresql.org/docs/9.1/static/datatype-datetime.html
Springboot version :1.5.7.RELEASE
Postgres JDBC Driver : 9.0-801.jdbc4
PostgresSQL DB: 9.6.5
Error:
org.postgresql.util.PSQLException: ERROR: operator does not
exist: timestamp without time zone == unknown
Hint: No operator matches the given name and argument type(s).
You might need to add explicit type casts.
Can anyone help?
The comparison operator in JPQL and SQL is = not ==
commit_ts==null
should be
commit_ts is null