How to do a timestamp comparison with JPA query? - jpa

We need to make sure only results within the last 30 days are returned for a JPQL query. An example follows:
Date now = new Date();
Timestamp thirtyDaysAgo = new Timestamp(now.getTime() - 86400000*30);
Query query = em.createQuery(
"SELECT msg FROM Message msg "+
"WHERE msg.targetTime < CURRENT_TIMESTAMP AND msg.targetTime > {ts, '"+thirtyDaysAgo+"'}");
List result = query.getResultList();
Here is the error we receive:
<openjpa-1.2.3-SNAPSHOT-r422266:907835 nonfatal user error> org.apache.openjpa.persistence.ArgumentException: An error occurred while parsing the query filter 'SELECT msg FROM BroadcastMessage msg WHERE msg.targetTime < CURRENT_TIMESTAMP AND msg.targetTime > {ts, '2010-04-18 04:15:37.827'}'. Error message: org.apache.openjpa.kernel.jpql.TokenMgrError: Lexical error at line 1, column 217. Encountered: "{" (123), after : ""
Help!

So the query you input is not JPQL (which you could see by referring to the JPA spec). If you want to compare a field with a Date then you input the Date as a parameter to the query
msg.targetTime < CURRENT_TIMESTAMP AND msg.targetTime > :param
THIS IS NOT SQL.

The JDBC escape syntax may not be supported in the version of OpenJPA that you're using. The documentation for the latest 1.2.x release is here: http://openjpa.apache.org/builds/1.2.2/apache-openjpa-1.2.2/docs/manual/manual.html#jpa_langref_lit .
The documentation mentioned earlier refers to the docs for OpenJPA 2.0.0 (latest): http://openjpa.apache.org/builds/latest/docs/manual/jpa_langref.html#jpa_langref_lit
That said is there any reason why you want to inject a string into your JPQL? What about something like the following snippet?
Date now = new Date();
Date thirtyDaysAgo = new Date(now.getTime() - (30 * MS_IN_DAY));
Query q = em.createQuery("Select m from Message m "
+ "where m.targetTime < :now and m.targetTime > :thirtyDays");
q.setParameter("now", now);
q.setParameter("thirtyDays", thirtyDaysAgo);
List<Message> results = (List<Message>) q.getResultList();

Related

HQL / Postgres : EXTRACT(dow from CURRENT_TIMESTAMP) with a parameter

I am trying to implement following query to retrieve users that need to be alerted weekly and send alerts on Monday.
Following query works :
String sql = "SELECT user_id FROM table"
+ " WHERE alert_interval = 7 AND EXTRACT(dow from CURRENT_TIMESTAMP) = 1))";
But if I want to pass a parameter to EXTRACT(dow from CURRENT_TIMESTAMP) like this :
String sql = "SELECT user_id FROM table"
+ " WHERE alert_interval = 7 AND EXTRACT(dow from : currentTs) = 1))";
var args = new MapSqlParameterSource("currentTs", currentTs);
jdbcOperations.query(sql, args, (ResultSet rs) -> ...);
I am getting following error :
nested exception is org.postgresql.util.PSQLException: ERROR: function pg_catalog.date_part(unknown, unknown) is not unique
Hint: Could not choose a best candidate function. You might need to add explicit type casts.
Not sure how I can pass a parameter to EXTRACT(dow from ...) ?
Any pointers would be appreciated.
Thank you.

how to use date_part in hibernate query?

String q = "select id from Calendar c " +
"where c.isActive = 1 and " +
"date_part('dow', '2017-09-19 13:23:23'::date) = c.frequencyValue)";
Query query = em.createQuery(q);
List results = query.getResultList();
If I include ::date, hibernate would complain because : conflicts with parameter, but if I don't, postgres will complain. Could not choose a best candidate function. You might need to add explicit type casts. What can I do?
https://docs.jboss.org/hibernate/orm/3.3/reference/en/html/queryhql.html#queryhql-expressions
as specified extract function should work if the underlying db supports them so:
extract(dow from date '2017-09-19 13:23:23');
should works

JPA Query with GROUP BY, HAVING and COUNT

So the query below is probably not the most efficient, buy still, I am wondering why it is returning no result, even though the SQL counterpart does. There is no error, I am just getting no result. Is it maybe not the correct equivalent for the query I wrote in MySQL?
This is the JPA JPQL.
Query query = em.createQuery("SELECT sub FROM Subscription sub WHERE "
+ "sub.isSuspended = 0 AND "
+ "(SELECT i FROM Invoice i WHERE i.dateDue < CURRENT_DATE AND i.datePaid IS NULL "
+ "GROUP BY i HAVING COUNT(i.idInvoice) > 2) MEMBER OF sub.invoices");
And this is the SQL from MySQL.
SELECT * from subscription
WHERE subscription.is_suspended = 0 AND id_subscription IN
(SELECT id_subscription FROM invoice
WHERE date_due < CURDATE() AND date_paid IS NULL
GROUP BY id_subscription
HAVING COUNT(*) > 2)
The two queries are not the same. To use the actual query use the NativeQuery createNativeQuery() instead of Query.
In your case the JPA version seems to have syntax errors.
After the AND you are missing the IN operator.
In the nested query you are selecting i instead of something like i.idInvoice
The JPA query should look like
SELECT sub FROM Subscription sub
WHERE sub.isSuspended = 0
AND sub.idSubscription IN
(SELECT i.idInvoice
FROM Invoice i
WHERE i.dateDue < CURRENT_DATE AND i.datePaid IS NULL
GROUP BY i.idInvoice
HAVING COUNT(i.idInvoice) > 2);

Error using CURRENT_TIMESTAMP() into a query

I want to compare two date in jpql query using the current date function
I got an error
Syntax error parsing [SELECT d FROM Dossier d WHERE d.depid=1 AND d.typeDossier = :tpd AND d.dateCreation < CURRENT_TIMESTAMP() + 5].
[105, 107] The left expression is not an arithmetic expression
This is my query:
public List<Dossier> getDossierFindAllParDepartementDBTECHandUrgen() {
return (List<Dossier>) em.createQuery("SELECT d FROM Dossier d WHERE d.depid=1 AND d.typeDossier = :tpd AND " +
"d.dateCreation < CURRENT_TIMESTAMP() + 5",
Dossier.class).setParameter("tpd", "Urgent").getResultList();
}
JPA supports function CURRENT_TIMESTAMP
https://en.wikibooks.org/wiki/Java_Persistence/JPQL#Functions
but will not work with arithmetic operations. You can solve the problem by using a parameter, for example
TypedQuery<Dossier> query = em.createQuery("SELECT d FROM Dossier d WHERE d.depid=1 AND d.typeDossier = :tpd AND " +
"d.dateCreation < :fiveDaysAhead",
Dossier.class);
Date myFiveDaysAhead = new Date(Calendar.getInstance().add(Calendar.DAYS_OF_YEAR,5).getTimeInMillis());//or something
query.setParameter("tpd", "Urgent");
query.setParameter("fiveDaysAhead", myFiveDaysAhead, TemporalType.TIMESTAMP);
It may also be possible to find vendor specific solutions, as i noticed in one other answer https://stackoverflow.com/a/18514326/2835455

JDBC setDate not working with preparedstatement

I am pretty new to Java JDBC. I am trying to create a JDBC preparedstatement to do a SELECT between two Oracle DATE values.
I know that data exists between these two times, as I can do the query directly.
When I execute the prepared statement from within my JDBC code, however, it returns 0 rows.
My input start and times are Long Unix time values in milliseconds.
I have tried to pare down the code to the bare minimum:
public static List<Oam1731Sam5Data> getData(Long startTime, Long endTime) {
try {
String query = "SELECT timecaptured from oam1731data5 " +
"WHERE timecaptured between ? and ?";
pstmt = conn.prepareStatement(query); // create a statement
Date javaStartDate = new Date(startTime);
Date javaEndDate = new Date(endTime);
pstmt.setDate(1, javaStartDate);
pstmt.setDate(2, javaEndDate);
ResultSet rs = pstmt.executeQuery();
while (rs.next()) {
String serviceId = rs.getString("SERVICEID");
String recordSource = rs.getString("RECORDSOURCE");
Date timeCaptured = rs.getDate("TIMECAPTURED");
Long oneWayMaxDelay = rs.getLong("ONEWAYMAXDELAY");
Long twoWayMaxDelay = rs.getLong("TWOWAYMAXDELAY");
Long twoWayMaxDelayVar = rs.getLong("TWOWAYMAXDELAYVAR");
Long packetLoss = rs.getLong("PACKETLOSS");
}
} catch (SQLException se) {
System.err.println("ERROR: caught SQL exception, code: " + se.getErrorCode() +
", message: " + se.getMessage());
}
The issue is that this returns 0 rows, where the same query returns data:
select insert_date, recordsource, serviceid, timecaptured, onewaymaxdelay, twowaymaxdelay, twowaymaxdelayvar, packetloss from oam1731data5
where timecaptured between to_date('2012-01-18 07:00:00','YYYY-MM-DD HH24:MI:SS')
and to_date('2012-01-18 08:00:00','YYYY-MM-DD HH24:MI:SS')
order by insert_date
DBMS_OUTPUT:
INSERT_DATE RECORDSOURCE SERVICEID TIMECAPTURED ONEWAYMAXDELAY TWOWAYMAXDELAY TWOWAYMAXDELAYVAR PACKETLOSS
1/18/2012 10:43:36 AM EV TAMP20-MTRO-NID1SYRC01-MTRO-NID1 1/18/2012 7:25:24 AM 40822 79693 343 0
1/18/2012 10:43:36 AM EV SYRC01-MTRO-NID1TAMP20-MTRO-NID1 1/18/2012 7:25:13 AM 39642 79720 334 0
1/18/2012 10:43:36 AM EV TAMP20-MTRO-NID1SYRC01-MTRO-NID1 1/18/2012
I have seen and ready many posts about problems somewhat like this, but have not been
able to find the key yet!
I thought about trying to make my query use a string and simply convert my dates to strings to be able to insert them for the Oracle TO_DATE function, but it seems like I should not have to do this.
And here is the output from my println statements. Is it an issue that the dates that print do NOT show the time portion?
SQL query: SELECT timecaptured from oam1731data5 WHERE timecaptured between ? and ?
Java Oracle date: 2012-01-18 end date 2012-01-18
Thanks in advance for any help.
Mitch
A java.sql.Date represents a date without time. You must use a Timestamp if you care about the time part.
The javadoc of java.sql.Date says:
To conform with the definition of SQL DATE, the millisecond values
wrapped by a java.sql.Date instance must be 'normalized' by setting
the hours, minutes, seconds, and milliseconds to zero in the
particular time zone with which the instance is associated.
Using Date for your start and end dates shouldn't cause any problem. The data in the specified interval should be retrieved in that time interval (Those data that you printed which were inserted on 1/18/2012 are supposed to be retrieved in this case.) Therefore I don't think that omitting time portion is your problem.