After switching from MySQL to PostgreSQL I found out that my SQL query (#Query in spring data repository interface) does not work anymore. The issue is caused by null value being sent as bytea and I'm getting following exception:
Caused by: org.postgresql.util.PSQLException: ERROR: operator does not exist: bigint = bytea
Hint: No operator matches the given name and argument type(s). You might need to add explicit type casts.
Repository with #Query:
public interface WineRepository extends PagingAndSortingRepository<Wine, Long> {
#Query(value = "SELECT * FROM WINE w WHERE (?1 IS NULL OR w.id = ?1)", nativeQuery = true)
Wine simpleTest(Long id);
}
Simple test:
LOGGER.warn("test1: {}", wineRepository.simpleTest(1L)); //ok
LOGGER.warn("test2: {}", wineRepository.simpleTest(null)); //PSQLException
In the real case I have multiple parameters which can be null and I would prefer not checking them in java code but sending them to sql query. I have checked questions here on stackoverflow but found none with a good answer especially for spring data repository #query annotation.
What is a correct way of handling null values with PostgreSQL? Or do you have any hints how to fix my approach? Thanks!
Update:
Issue seems to be related to nativeQuery = true, when value is false, null values work as expected. So the question is whether it is possible to make it function even with nativeQuery enabled.
Try this.
SELECT *
FROM WINE w
WHERE ?1 IS NULL OR w.id = CAST(CAST(?1 AS TEXT) AS BIGINT)
It satisfies the type checker and should have the same properties as the original query. CAST is not a big performance hit if it happens on a constant value rather than a value from a database row.
You are trying to check whether a Java null is equal to Postgres NULL which I think is not necessary. You can rewrite as following and avoid sending null to the simpleTest function at all.
#Query(value = "SELECT * FROM WINE w WHERE (w.id IS NULL OR w.id = ?1)", nativeQuery = true)
Wine simpleTest(Long id);
I had a similar issue in an #Query where a Long was being interpreted by Postgres as a bytea. The solution for me was to unbox the #Param...by passing a long value, Postgres correctly interpreted the value as a bigint
I know it is an old issue, but you should be able to fix that bay casting the java value. Something like:
public interface WineRepository extends PagingAndSortingRepository<Wine, Long> {
#Query(value = "SELECT * FROM WINE w WHERE (?1\\:\\:bigint IS NULL OR w.id = ?1\\:\\:bigint)", nativeQuery = true)
Wine simpleTest(Long id);
Related
I am trying to call a basic stored procedure from azure sql which is just returning the number 1, which looks something like this
CREATE PROCEDURE [dbo].[testProc]
#TableName varchar(100)
AS
BEGIN
SET NOCOUNT ON
SELECT 1
END
I have a spring boot app trying call the stored procedure using the #Query annotation
#Repository
#Transactional
public interface TestDAO extends JpaRepository<TestEntity, Long> {
#Query(value = "CALL testProc(:TableName)", nativeQuery = true)
Long invokeTestProc(#Param("TableName") String TableName);
}
however, I get an exception which says
"Incorrect syntax near '#P0'"
and SQLGrammarException: could not extract ResultSet.
I am not sure how to fix this, I tried using the #Procedure with #NamedStoredProcedureQueries annotations and it threw another exception saying "Cannot mix positional parameter with named parameter registrations;"
According to the documentation for Azure Sql, you have to call the stored procedure with the following
EXECUTE HumanResources.uspGetEmployeesTest2 N'Ackerman', N'Pilar';
-- Or
EXEC HumanResources.uspGetEmployeesTest2 #LastName = N'Ackerman', #FirstName = N'Pilar';
So in your case this would be translated as
#Query(value = "EXECUTE testProc :TableName", nativeQuery = true)
Long invokeTestProc(#Param("TableName") String TableName);
or
#Query(value = "EXEC testProc #TableName = :TableName", nativeQuery = true)
Long invokeTestProc(#Param("TableName") String TableName);
So considering that you use native queries, the
exception which says "Incorrect syntax near '#P0'" and
SQLGrammarException: could not extract ResultSet.
reffers to the wrong use of sql grammar where you don't use execute or exec and you also pass the parameter in a way not expected for Azure SQL.
I am trying to get the size of a row in a postgresql table, I found that pg_column_size would do the trick, but its not working with hibernate :
#Query("SELECT pg_column_size(t.*) as filesize FROM TABLE as t where name=:name")
int getSize(#Param("name") String name);
intellij is giving this error :
< operator > or AS expected, got '('
I guess the problem is that hibernate doesnt support specific postgresql queries, it only supports the basic sql queries.
so is there a way around this ? if not is there a way to get/estimate the size of a postgresql row in java ?
In order to use a built-in postgres function , you have to declare you JPA query as nativeQuery
you should first change query to native (hibernate will directly execute the query instead of jpa -> sql generation )
#Query(value="SELECT pg_column_size(t.*) as filesize FROM TABLE as t where name=:name",nativeQuery=true)
int getSize(#Param("name") String name);
Also be sur of the TABLE name to be correct .
Add nativeQuery = true after the native query.
#Query("SELECT pg_column_size(t.*) as filesize FROM users as t where t.name=:name",nativeQuery = true)
use above Query, Hope This will work.
Ok, I'm rather new to Spring Boot and I'm currently facing a problem with attempting to successfully run the following Query towards my MySQL-Database in Postman without receiving a list of null-values:
#Query("SELECT user.id, user.email FROM ProjectUser user WHERE user.lastLogin IS NOT NULL AND user.userType = 'Test'")
List<ProjectUser> test1();
User.id and user.email is of type bigint(20) and VARCHAR(255) in the database, and I suspect that it might be related to them not being declared as NVARCHAR, but I have not tried modifying this as I'm scared it will corrupt the database :P. However, I see that the conditioning works as the number of objects returned is correct, so I'm suspecting that the issue is mainly related to representing the values.
In comparison, the following NativeQuery works and returns non-null values in Postman:
#Query(value = "SELECT id, email FROM user WHERE last_login IS NOT NULL AND user_type = 'Test'", nativeQuery = true)
List<ProjectUser> test1();
I was just wondering if anyone here might have an idea to how to fix this problem? I don't necessarily see why I should not go with the NativeQuery-solution when it works, but it is more of curiosity and understanding the problem.
You should strictly distinguish hql/jpql query and native sql query.
When you use explicit list of columns in jpql select clause that means you should expect result as List<Object[]>.
So, for your case, you should correct the query in the following way:
#Query("SELECT user FROM ProjectUser user WHERE user.lastLogin IS NOT NULL AND user.userType = 'Test'")
List<ProjectUser> test1();
For more detailed explanation about values you can use in SELECT clause see the documentation.
I am trying to update the entities in a certain list using Spring Data JPA. However, I'm getting SQL Grammar Exception.
This is the method:
#Modifying
#Query("UPDATE Call c set c.locationLocked = false, c.locationLockedBy = null, c.locationLockedOn = null WHERE c.callIdentifier IN :timedOutLockedCallsIdentifiers AND c.audit.retired = false")
int expireTimedOutLockedCalls(#Param("timedOutLockedCallsIdentifiers") List<String> timedOutLockedCallsIdentifiers);
And this is the root cause:
Caused by: org.postgresql.util.PSQLException: ERROR: syntax error at or near ")"
You are passing null or empty list in IN clause of the query.
Please check timedOutLockedCallsIdentifiers before calling above query.
It would probably be easier to do it more programatically then in a query
In your repository have the following
#Query("SELECT c FROM Call c WHERE c.callIdentifier IN :timedOutLockedCallsIdentifiers AND c.audit.retired = :retired")
List<Call> findByCallIdentifiers(List<String> timedOutLockedCallsIdentifiers, Boolean retired)
Then in your class
List<Call> updated = callRepo.findByCallIdentifiers(identifiers, false)
for(Call c : updated) {
c.setLocationLocked(false);
c.setLocationLockedBy(null);
c.setLocationLockedOn(null);
}
callRepo.saveAll(updated);
This will call the proper underlying updates as needed and cant let spring handle the query syntax properly
I am trying to truncate a date, and group by the values.
I have tried this:
JPA
select v.sop, FUNC('DATE_TRUNC', 'day', v.scrappedAt) as dt, sum(v.totalValue)
from TABLE v
where v.coordStatus like 'done%' and (:plant is null or v.target =
:plant) and v.scrappedAt is not null
group by v.sop, dt
I have also tried grouping by the FUNC
select v.sop, FUNC('DATE_TRUNC', 'day', v.scrappedAt) as dt, sum(v.totalValue)
from TABLE v
where v.coordStatus like 'done%' and (:plant is null or v.target =
:plant) and v.scrappedAt is not null
group by v.sop, FUNC('DATE_TRUNC', 'day', v.scrappedAt)
The error is the same
org.postgresql.util.PSQLException:
ERROR: column "t_mcp_verschrottungs_db_sharepoint.scrappedat"
must appear in the GROUP BY clause or be used in an aggregate function
Strictly speaking, if FUNC were a native Postgres function, then your query should be working, and the query would be ANSI compliant. After reading this SO question, it appears that Postgres can't figure out that the two FUNC calls are actually the same thing. Perhaps you can rephrase the sum using a correlated subquery:
SELECT
v1.sop,
FUNC('DATE_TRUNC', 'day', v1.scrappedAt) AS dt,
(SELECT SUM(totalValue) FROM TABLE v2
WHERE v2.sop = v1.sop AND
FUNC('DATE_TRUNC', 'day', v2.scrappedAt) =
FUNC('DATE_TRUNC', 'day', v1.scrappedAt) AND
v2.acoordStatus LIKE 'done%' AND
(:plant is null OR v2.target = :plant) AND
v2.scrappedAt IS NOT NULL) value_sum
FROM TABLE v1
WHERE
v1.coordStatus LIKE 'done%' AND
(:plant is null OR v1.target = :plant) AND
v1.scrappedAt IS NOT NULL;
An alternative to the above, should it either not work or not be performant, would be to use a native Postgres query.