Hibernate Criteria - How to apply functions to ORDER BY? - java

It sounded like a simple problem, but I found no easy solution online.
I am trying to replicate the current ORDER BY in Hibernate, without succes:
SELECT * FROM IPEM.DEMANDE
WHERE INIT_DATE >= TO_TIMESTAMP('23/04/2021', 'dd/MM/yyyy') AND INIT_DATE <=
TO_TIMESTAMP('29/04/2021', 'dd/MM/yyyy')
ORDER BY TO_TIMESTAMP(LIMIT_DATE, 'dd/MM/yyyy'), TO_TIMESTAMP(INIT_DATE , 'dd/MM/yyyy') ASC <<< this line
Why ? Because I have this kind of data in my database:
05/05/2021 00:00:00 - 23/04/2021 00:00:00
05/05/2021 00:00:00 - 28/04/2021 00:00:00 << this should be 3rd
05/05/2021 02:00:00 - 24/04/2021 00:00:00 << this should be 2nd
The hours mess up the sorting. I'm trying to ignore them/format the date before loading my entries. A way I found to do so is applying TO_TIMESTAMP to ORDER_BY. It works well in SQL, but when going to Hibernate, it is not that simple.
Actually, my code looks like this:
criteria.addOrder(Order.asc(fieldName));
I tried some trivial solution, which obviously did not work:
criteria.addOrder(Order.asc("TO_TIMESTAMP(" + fieldName + ", 'dd/MM/yyyy')");
For which I had the following error (limitDate is the Java name, LIMIT_DATE the corresponding column):
org.hibernate.QueryException: could not resolve property: TO_TIMESTAMP(limitDate, 'dd/MM/yyyy')
How can I apply TO_TIMESTAMP to the members of my ORDER BY ? (No worries, we assume here all the members are dates, both in Java and in the SQL Table).

That's not possible, but you can subclass Order and override the org.hibernate.criterion.Order#toSqlString method to implement whatever logic you need. Anyway, you should move away from the legacy Criteria API to the JPA Criteria API. There you could use criteriaQuery.orderBy(criteriaBuilder.function("TO_TIMESTAMP", root.get(fieldName), criteriaBuilder.literal("dd/MM/yyyy")))

Related

Criteria JPA - Call Postgres CAST function

I'm trying to call a Postgres function with Criteria but it's not working. I need to use the LIKE clause in a UUID field, so I need to convert into VARCHAR first.
The result I need:
SELECT * FROM my_table WHERE cast(uuid as varchar(36)) like '%1234%';
What I'm doing in Criteria:
final Path<UUID> uuidField = from.get("uuid");
var cast = cb.function("cast", String.class, uuidField, cb.literal("as varchar(36)"));
cb.like(cast, String.format("%%%s%%", stringValue));
The query which is being generated:
HQL: select generatedAlias0 from com.MyTable as generatedAlias0 where function('cast', generatedAlias0.uuid, 'as varchar(36)') like '%1234%' order by generatedAlias0.name asc
Error:
2022-08-08 18:38:48,549 WARN [io.ver.cor.imp.BlockedThreadChecker] (vertx-blocked-thread-checker) Thread Thread[vert.x-eventloop-thread-9,5,main] has been blocked for 2393 ms, time limit is 2000 ms: io.vertx.core.VertxException: Thread blocked
at antlr.ASTFactory.make(ASTFactory.java:342)
at antlr.ASTFactory.make(ASTFactory.java:352)
at org.hibernate.hql.internal.antlr.HqlBaseParser.jpaFunctionSyntax(HqlBaseParser.java:4633)
at org.hibernate.hql.internal.antlr.HqlBaseParser.primaryExpression(HqlBaseParser.java:1075)
The log is not so clear (I'm using Quarkus + Hibernate Reactive), but I suspect it crashed in database because the function('cast', generatedAlias0.uuid, 'as varchar(36)').
I think it should be something like: function('cast', generatedAlias0.uuid, as varchar(36)) (without quotes). But I don't know how to achieve this result to test my theory.
How can I call this CAST function?
After investigating some possible solutions (I'm avoiding to create custom database routines) I found something interesting in a answer from another question:
Currently JPA does not have APIs for replace() and cast(string as numeric). But you can use CriteriaBuilder.function(...) to create database native functions if database portability is not critical.
Source: JPA criteria builder: how to replace and cast a string to numeric in order-by?
I don't know if this is documented is some place, but assuming that there is no way to call CAST(x AS y) using Criteria, I tried a workaround to force the UUID to VARCHAR cast without using the probably unsupported CAST function.
I tested this direct SQL query to database:
SELECT * FROM my_table WHERE concat(uuid, '') like '%123%';
And it works. This CONCAT forces the cast to VARCHAR and the LIKE function does his job. Knowing this, I did:
final Path<UUID> uuidField = from.get("uuid");
var cast = cb.function("concat", String.class, uuidField, cb.literal(""));
cb.like(cast, String.format("%%%s%%", stringValue));
Worked perfectly. I hope this help someone else.
As #HaroldH said, it's a weird requirement, but happened in my project.

Mysql alike date functions in Morphia

I am in progress of migration a JPA/MySQL application to mongodb using morphia. I have some queries like
AND DATE(NOW()) > (DATE(created) + 2)
or
AND ( TIMESTAMPDIFF(MINUTE,kickoff,now()) > 1 )
or
AND DATE(ending) = DATE(NOW())
Is there anything similar in morphia or mongodb?
From your question I understand that you ask for a way to create date queries in MongoDB and how would your write that query in Morphia
The Cookbook for date queries can be found here.
As pointed in the above post, you should use a range query for this.
In MongoDB's shell, for your first query, you would write it like this:
// hope I got the date part right XD
db.posts.find({created: {$lt: new Date().getDate() - 2}});
For range queries morphia has 2 ways:
using the filter method
using directly the comparator methods.
So the first query would become something like this:
myObjDao.createQuery().field("created").lessThan(new Date(System.currentTimeMillis() - 2 * 24 * 3600 * 1000 )).fetch();

IntelliJ IDEA code inspection: HQL custom dialect & registered functions

My question is about
using registered functions for date/time manipulations in Hibernate Query Language and
IntelliJ IDEA's code inspection for these registered functions in HQL.
I'm using Hibernate 4.2.5 with Java 7, and SQL Server 2008 R2 as the database, and IntelliJ IDEA 12.1.6.
In an HQL query I need to perform the TSQL DATEADD function - or the equivalent HQL date operation. This doesn't seem to exist.
Here's what I'd like to achieve:
update MyTable set startTime = GETDATE(), targetTime = DATEADD(HOUR, allocatedTime, GETDATE()), endTime = null where faultReport.faultReportId = :faultReportId and slaTypeId = :slaTypeId
Searching for answers online has been disappointingly no help, and the most common advice (like the comment seen here: https://stackoverflow.com/a/18150333/2753571) seems to be "don't use date manipulation in hql." I don't see how I can get around performing the operation in the SQL statement in the general case (e.g. when you want to update one column based on the value in another column in multiple rows).
In a similar fashion to the advice in this post: Date operations in HQL, I've subclassed a SQLServerDialect implementation and registered new functions:
registerFunction("get_date", new NoArgSQLFunction("GETDATE", StandardBasicTypes.TIMESTAMP)); // this function is a duplication of "current_timestamp" but is here for testing / illustration
registerFunction("add_hours", new VarArgsSQLFunction(TimestampType.INSTANCE, "DATEADD(HOUR,", ",", ")"));
and added this property to my persistence.xml:
<property name="hibernate.dialect" value="my.project.dialect.SqlServerDialectExtended" />
and then I'm testing with a simple (meaningless, admitted) query like this:
select x, get_date(), add_hours(1, get_date()) from MyTable x
The functions appear to be successfully registered, and that query seems to be working because the following SQL is generated and the results are correct:
select
faultrepor0_.FaultReportSLATrackingId as col_0_0_,
GETDATE() as col_1_0_,
DATEADD(HOUR,
1,
GETDATE()) as col_2_0_,
... etc.
But I now have this problem with IntelliJ IDEA: where get_date() is used in the HQL, the code inspection complains "<expression> expected, got ')'". This is marked as an error and the file is marked in red as a compilation failure.
Can someone can explain how to deal with this, please, or explain what a better approach is? Am I using the incorrect SQLFunction template (VarArgsSQLFunction)? If yes, which is the best one to use?
I'd like the usage of the registered function to not be marked as invalid in my IDE. Ideally, if someone can suggest a better way altogether than creating a new dialect subclass, that would be awesome.

how to use date() in hibernate criteria

I would like to use Date() function in hibernate criteria, since one field is timestamp.
My sql would be:
"select * from feed where DATE(date) = DATE(NOW()) + INTERVAL 5 DAY"
When I try in criteria:
critera.add(Restrictions.eq("DATE(date)", "DATE(NOW()) + INTERVAL 5 DAY"));
I get:
could not resolve property: DATE(date) of: com.mycomapany.model.Feed
I have there in feed a field by the name date.
What is the problem? I am using MySQL
The best solution is to use the criteria api which you started to use.
Calendar c = Calendar.getInstance(LOCALIZATION);
//Add 5 Days
c.add(Calendar.DATE, 5);
criteria.add(Expression.ge("dateColumn",c.getTime());
You could probably find a way to make this work, but in my opinion, this is the wrong way to approach it.
One of the major features of Hibernate is the abstraction of the database-vendor-specific features, via the Hibernate Dialect.
You are attempting to access MySQL features directly, but through the vendor-neutral interface, thus your difficulty. Hibernate could have support for date intervals , but does not - see https://hibernate.onjira.com/browse/HHH-2434
So, the options seem to be:
Calculate the date ranges in your code, rather than in the HQL
Do what you need to do in SQL rather than HQL
Create a custom Hibernate Dialect, as in Performing Date/Time Math In HQL?
Using Joda Time:
public List<Feed> findByDaysFromToday(int daysFromToday) {
return em.createQuery(
"SELECT f FROM Feed f WHERE f.date BETWEEN :start AND :end")
.setParameter("start",
new LocalDate()
.toDateMidnight()
.toDate(),
TemporalType.DATE)
.setParameter("end",
new LocalDate().plusDays(daysFromToday)
.toDateMidnight()
.toDateTime()
.minusMillis(1)
.toDateTime()
.toDate(),
TemporalType.DATE)
.getResultList();
}

Why parameter substitution in HQL-Query failed but native type succeeds?

did one of you ever had the problem that parameter substitution in a HQL-Query didn't worked?
I'm using hibernate for some, I think, simple queries getting entities from my MySQL Database.
Here are the Queries:
entityManager.
createQuery("SELECT c FROM PersistentEvent c WHERE c.sessionId LIKE :sessionId").
setParameter("sessionId", session.getId()).getResultList();
Doesn't get a result! Under debugging I saw session.getId() returns the right value.
entityManager.
createQuery("SELECT c FROM PersistentEvent c WHERE c.sessionId LIKE :sessionId").
setParameter("sessionId", "TestSessionId - 1").getResultList();
Works for my testdata where such an entry exists.
Any suggestions? Thanks
After you posted the query Hibernate was generating, I noticed that session.getId() was inserting TestSession - 1 instead of TestSessionId - 1, which was in your hard coded example.

Categories

Resources