Optional parameters in named query in EclipseLink - db2

I am new to JPA named queries using EclipseLink and I want to "ignore" properties with null values in named query. I know that my question has been answered many times.
e.g. JPA Query to handle NULL parameter value
However, in my case following format is not working
+ " AND (:quoteNumber IS NULL OR ord.quoteNumber = :quoteNumber)"
I am getting error 'ILLEGAL USE OF KEYWORD NULL'. I will be using CriteriaQuery now and just curious why it is not working in named query.
Following are the DB2 and Eclipselink versions being used.
eclipselink: 2.5.1
DB2: DSN11015

The JPA Specification says that
3.8.13 Named Queries
Named queries are static queries expressed in metadata. Named queries can be defined in the Java Persistence query language or in SQL. Query names are scoped to the persistence unit.
So you can't really expect them to change on runtime based on some null condition. Criteria Query, as you point out, is dynamic by nature, so would be the way to go.
EDIT based on comment:
AND (ord.quoteNumber = :quoteNumber or :quoteNumber is null or :quoteNumber = ''
does not change the query on runtime (does not skip the clause). It evaluates the clause as TRUE. The problem with DB2 (and Derby as far as I know) is, that they do not allow "non-typed Null to be sent to the backend" as per API PreparedStatement.setObject. You can test it by setting the type via casting
AND (ord.quoteNumber = cast(:dfdTxt as integer) or cast(:dfdTxt as integer) is null or cast(:dfdTxt as integer) = ''
So this approach is DB Implementation specific and might change at some point.

Related

EFCore 3.1 FromSqlRaw is not working and throwing a bizarre error message

I have the most bizarre issue with EF Core 3.1. In EF Core 2.2 I used to be able to execute stored procedures. I see there is a breaking change in the documentation but, I am following the documentation exactly and it is not working. I have no nulls anywhere in the returned data. The NoticeOfInspection object matches the returned data exactly. What on Earth did they change that this is not working?
var data = _dbContext.NoticeOfInspections.FromSqlRaw("EXEC dbo.NewReportApp_NoticeOfInspection {0}", FacilityId).Single();
The error message is not helpful at all. First with the above line, it says, "InvalidOperationException: FromSqlRaw or FromSqlInterpolated was called with non-composable SQL and with a query composing over it. Consider calling AsEnumerable after the FromSqlRaw or FromSqlInterpolated method to perform the composition on the client side."
What?
So, I add AsEnumerable and then it throws, "InvalidCastException: Unable to cast object of type 'System.Int32' to type 'System.String'."
What on Earth have they done. This is not intuitive at all.
FromSqlRaw or FromSqlInterpolated was called with non-composable SQL
The non-composable SQL is the one which cannot be converted to subquery select * from (your_sql). Calling SP (EXEC …) is one of the non-composable constructs.
and with a query composing over it
Non query returning LINQ operators like Single, First, Count, Max, Sum etc. require composing over the provided SQL query, for instance select count * from (your_query).
You can read more about it in Raw SQL queries - Composing with LINQ documentation topic, which also contains the "calling SP" and other limitations/restrictions:
Composing with LINQ requires your raw SQL query to be composable since EF Core will treat the supplied SQL as a subquery. SQL queries that can be composed on begin with the SELECT keyword. Further, SQL passed shouldn't contain any characters or options that aren't valid on a subquery, such as:
A trailing semicolon
On SQL Server, a trailing query-level hint (for example, OPTION (HASH JOIN))
On SQL Server, an ORDER BY clause that isn't used with OFFSET 0 OR TOP 100 PERCENT in the SELECT clause
SQL Server doesn't allow composing over stored procedure calls, so any attempt to apply additional query operators to such a call will result in invalid SQL. Use AsEnumerable or AsAsyncEnumerable method right after FromSqlRaw or FromSqlInterpolated methods to make sure that EF Core doesn't try to compose over a stored procedure.
With that being said, inserting AsEnumerable() before Single() should really work.
The new exception you are getting is either EF Core bug or data type mapping issue (either you are passing int to string parameter, or SP is returning int for string class property). You need to examine the exception stack trace and/or compare your SP parameter and column types to FacilityId argument type and NoticeOfInspection class property types/mappings.

Spring Data: Getting NonUniqueResult Problem for the query

Hello experts of the world. Need some help concerning executing a query with SpringData.
The expectation is to execute the Query below in the Spring Data annotation by combining with the repository method name (Automated Query Construction) to get a unique result. Apparently it fails from time to time by saying the result is not Unique.
The question here is if the method name is still considered in Query Construction while also executing the query in the annotation.
#Query("SELECT r from Revision r WHERE r.revisionBid = ?1 AND r.revisionStatusId = ?2 ORDER BY r.lastModifiedDate DESC")
Optional<Revision> findFirst(Integer revisionBid, Integer revisionStatusId);
Thanks in advance!
The query creation for limiting to 1 result is defined here with FIRST & TOP included in the method name.
https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#repositories.query-methods.query-creation
I don't think "findFirst" will work when you're using an #Query, as the query will be constructed from HQL expression in the #Query rather than the fluent API passing over the method name. Because of this, when the query returns multiple results, it will throw the exception as the Optional is told to wrap a single returned object, not a collection. Add a LIMIT clause to the HQL query and you should be good.

Using jsonb_set in update with jOOQ

I have a sql query for updating a status value in a data column of type
jsonb in Postgresql that looks like this:
update sample
set updated = now(),
data = jsonb_set(data, '{status}', jsonb 'CANCELLED', true)
where id = 11;
I need to translate that to a working jOOQ query in my Kotlin project ... I
have this so far:
jooq.update(Tables.SAMPLE)
.set(Tables.SAMPLE.UPDATED, OffsetDateTime.now())
.set(Tables.SAMPLE.DATA, field("jsonb_set(data, '{status}', jsonb '\"CANCELLED\"', true)"))
.where(Tables.SAMPLE.ID.eq(id))
.execute()
But the second set fails with None of the following functions can be called with the
arguments supplied error message... What is the correct signature of set
that I can use here?
I am basing my jOOQ syntax on the answer that Lukas Eder provided in Using raw value-expressions in UPDATE with jooq
In an UPDATE statement, you have to match data types in your SET clause on both sides. I.e. SAMPLE.DATA is of type Field<T>, so the expression you're setting it to must also be of type Field<T>.
I'm assuming that SAMPLE.DATA is a Field<JSONB>, so it will be sufficient to write
.set(SAMPLE.DATA, field("json_set(...)", JSONB.class))
Notice that jOOQ 3.12 has introduced this JSONB type. In previous versions, lacking any out-of-the-box jOOQ representation for JSON and JSONB types, the jOOQ code generator may have generated a Field<Object> type for your SAMPLE.DATA column, in case of which your statement would have compiled.

Is 'jdbcType' necessary in a MyBatis resultMap?

When we use Mybatis , in <select> ...</select> statment I know we need set jdbcType beacuse the IN variable maybe null, but when I see the document of Mybatis, I found jdbcType in <result>...</result> under ResultMap. the document of the
jdbcTpe in <result>...</result> was:
... The JDBC type is only required for nullable columns upon insert, update or delete. This is a JDBC requirement, not a MyBatis one. So even if you were coding JDBC directly, you'd need to specify this type – but only for nullable values.
the bold word say only required for nullable columns upon insert, update or delete.
BUT,the element of result is used in select neither insert, update or delete.
so ,is it necessary use jdbcType in <result>...</result> ?
Most of the time, no. Why? Read on.
If you want to use a null as a JDBC parameter value you need to specify the jdbcType. That's a restriction of the JDBC specification you can't avoid. Therefore, if there's even a remote possibility a JDBC parameter could have a null value, then yes, specify it.
This does not apply to parameters preprocessed by MyBatis inside MyBatis tags, like the ones you use in the "test" attribute of the < if > tag. Those ones are not JDBC parameters.
Now, for the columns you read. These are the ones you are interested on. The thing is most of the time you don't need them. MyBatis will pick the right JDBC type for you. Well... this has been the case for me 99.999% of the time.
What about the other 0.001%? For some exotic column types -- that you rarely use -- MyBatis may pick the wrong JDBC type for you. The designers of MyBatis thought about this case, and give you the chance of overriding it. I think I remember an XML type database column that MyBatis was unsuccessfully trying to read as a VARCHAR, but I don't remember which database.
Bottom line, don't use it when reading columns, unless MyBatis reads exotic data type columns (XML, UUID, POINT, etc.) the wrong way.

Query annotation not working for max(id)

I have a domain object GenJournal and it has an "id" member (Long) that's auto-generated. I also have a JPA repository that I've added ...
#Query("select coalesce(max(u.id), 0) from GenJournal u")
Long getMaxId();
The method getMaxId() returns zero or null before I added coalesce. I have two rows in my database with ids 1 and 2. Can anyone help me determine why this doesn't work?
I'm trying to get the latest or max id so that I can use the find method after to return the most recent GenJournal object in my service class.
I'm stumped and really need some options or strategy to determine why this doesn't work.
You could use "Native Query" feature by passing nativeQuery = true param into #Query annotation like this
#Query("select coalesce(max(u.id), 0) from Gen_Journal_Table u", NativeQuery = true)
Long getMaxId();
My issue was two-fold. First I was getting null without the use of "coalesce". That caused me to think that this didn't work. When I adopted the use of "coalesce" I didn't realize that my table had no records and was returning the zero (0). My table in the production profile did have two records and I was expecting an id of 2.
I was manually checking the wrong database and setting expectations that were incorrect.