Java Persistence Criterias; Cast Expression<Timestamp> to Expression<Long>? - java

I am using the CriteriaBuilder and CriteriaQuery to build my query to the database, but I have encountered an issue that I do not know how to solve, since I am very new to this whole ordeal called JPA.
In Java, I have a property called timestamp for a class called Report, and it is set to the same corresponding #TemporalType.
I also have a class called Affiliate which has a list of Report objects.
In my query, I want to fetch all the Affiliate objects that do not have a Report in the last Affiliate.maxSilenceMinutes.
My questions:
Are there any ways in standardized JPA to modify dates? Like a CriteriaBuilder.subtractMilliseconds(Expression<Timestamp>, Long) of sorts?
If not, is there a way to cast Expression<Timestamp> to Expression<Long> so that I can subtract on a currentTimestamp literal to get the minimum value for a CriteriaBuilder.lessThanOrEqualTo(greatestReportTimestampMs, minimumAllowedMs)?
I know this might feel like a confusing question, but the main part is simply: Is it possible to go Expression<Timestamp> to Expression<Long>? It throws an exception for me if I try to use the .as(Long.class) method, but which should be the default underlying data type in most DBs anyway?
Hope you guys can help, since I feel kind of stuck :)

If you know the value you want to subtract at the time of querying,
you can subtract beforehand:
Calendar c = new Calendar();
c.setTime(timestamp.getTimestamp());
c.add(DAY, - someNumberOfDays); //or whatever unit you want
Date d = c.getTime();
If not, you probably need to call a database function to do the subtraction, via
CriteriaBuilder.function()
CriteriaBuilder.lessThanOrEqual() works on Comparables. Timestamps are comparable. So you could construct a Timestamp via new Timestamp(long ms)
and compare it with the other expression.
I hope this helps.

This is not built into Hibernate, so you will need a custom function of some kind.
The JDBC standard includes a function escape {fn TIMESTAMPADD( SQL_TSI_SECOND, secs, timestamp)} which should be translated into the correct SQL for the target database, but not all JDBC implementations provide it. There is therefore a chance you can add a custom StandardJDBCEscapeFunction to Hibernate's Dialect to get the result you need.
If you don't have that available, you'll have to find out what the correct database specific implementation is and there is a lot of variability here. For example:
Oracle: (timestamp + secs/86400)
SQLServer: DATEADD(ss,secs,timestamp)
DB2: (timestamp + secs SECONDS)
MySQL: DATE_ADD(timestamp, INTERVAL secs SECONDS)
Once you know it, you can use the correct expression as an SQL criteria.
The fact that date-time manipulation is not standardised in the Dialect and not fully implemented in many JDBCs means that what you are trying to do will be very difficult to write in a database neutral way.

Related

MySQL's TIME() function in h2 with Java

So, I need to translate the following query into Java
SELECT * FROM table WHERE TIME(date) BETWEEN TIME(startTime) AND TIME(endTime)
where date is a datetime column. Both startTime and endTime are local variables.
The technologies that I'm using right now are Spring and Hibernate.
The following solution
query.add(
Restrictions.sqlRestriction(
"TIME(date) between TIME('?') and TIME('?')", values, types
);
, where the values variable is an array with startTime and endTime and the types variable is an array with the values's respective types (string), unfortunately is not compatible with H2, since the TIME function wasn't implemented. I do know that the HOUR, MINUTE and SECOND functions are implemented and working.
I haven't figured out a way of implementing it myself and couldn't find any solution online. Does anyone have any idea of how I can overcome this?
If you read through the HQL language specification, you will find that it is largely devoid of any support for date/time functions. This is mainly due to that every database has its own way of handling date and time, so there is little common support. However, the CAST function is supported by HQL, assuming the underlying database also supports it. From the H2 documentation, we find this example of casting to time:
CAST(TIMESTAMP '2010-01-01 10:40:00.123456' AS TIME(6))
So, you may try doing this in your HQL query:
SELECT *
FROM table
WHERE
CAST(date AS TIME(6)) BETWEEN CAST(startTime AS TIME(6)) AND
CAST(endTime AS TIME(6));

java criteria Date comparison

I am filtering a search with criteria object.
but the filter doesn't work for date.
I made this for instance :
criteria.add(Restrictions.and(
Restrictions.like("serialNumber", device.getSerialNumber()),
Restrictions.like("installDate", device.getInstallDate()), // a date
Restrictions.like("ipAdress", device.getIpAdress())));
then i made this :
else if (device.getInstallDate() != null) {
criteria.add(Restrictions.like("installDate", device.getInstallDate()));
}
Do you have any idea to filter by date ?
Your code/approach looks fine. You may want to enable SQL logging to see what statements exactly are sent to the DB and what values are bound to the statement parameters. This should help you figure out the issue (the issue may be just some detail like e.g. dates with/without time parts, or something similar).
See also:
Hibernate show real SQL
To search for exact dates I use:
criteria.add(Restrictions.eq("installDate", device.getInstallDate()));
Note also that dates and timestamps are treated differently by the underlying database based on the corresponding SQL types. A field declared as a date will not include hours/minutes/etc. If the desire is to compare both date and time, be sure to use timestamp in the Hibernate declaration.
The fastest way to show the SQL statements is to set the show_sql property to true in your Hibernate configuration

On Google App Engine is it possible JDOQL Date without using a parameterised query?

Is it possible to do a date query in JDOQL without using a parameterrised query on Google App Engine.
I am trying to write some generic code that looks something like this, where criteria is just a string, and I would like to be able to specify anything - with this piece of code not needing to to know much about the underlying data.
Query query = pm.newQuery(tClass);
if (criteria!=null) {
query.setFilter(criteria);
}
criteria could be "startdate = 'someproperlyformatteddatetime'"
Thanks for your suggestions.
Of course, GAE JDO queries support JDOQL. You could simply do something like this: q.setFilter("height <= 200") or q.setFilter("name == 'Smith'"), where you would programmatically assemble the JDOQL filter string. The only downside is that you need to know the type of parameters (as saved in Datastore), as strings need to be enclosed in single or double quotes.
Note that all restrictions on queries still apply.
Also, if you want to query on multiple properties where you also use inequality operator, then you need to define compound indexes beforehand.
Update: JDOQL literal parameter specification works with string and numeric values; all other value types must use parameter substitution. You could still do that programmatically.
Another workaround would be if you use long instead of Date and convert dates to UNIX timestamps (which are of type long).

Avoid implicit conversion from date to timestamp for selects with Oracle using Hibernate

I'm using Hibernate 3.2.7.GA criteria queries to select rows from an Oracle Enterprise Edition 10.2.0.4.0 database, filtering by a timestamp field. The field in question is of type java.util.Date in Java, and DATE in Oracle.
It turns out that the field gets mapped to java.sql.Timestamp, and Oracle converts all rows to TIMESTAMP before comparing to the passed in value, bypassing the index and thereby ruining performance.
One solution would be to use Hibernate's sqlRestriction() along with Oracle's TO_DATE function. That would fix performance, but requires rewriting the application code (lots of queries).
So is there a more elegant solution? Since Hibernate already does type mapping, could it be configured to do the right thing?
Update: The problem occurs in a variety of configurations, but here's one specific example:
Oracle Enterprise Edition 10.2.0.4.0
Oracle JDBC Driver 11.1.0.7.0
Hibernate 3.2.7.GA
Hibernate's Oracle10gDialect
Java 1.6.0_16
This might sound drastic, but when faced with this problem we ended up converting all DATE columns to TIMESTAMP types in the database. There's no drawback to this that I can see, and if Hibernate is your primary application platform then you'll save yourself future aggravation.
Notes:
The column types may be changed with
a simple "ALTER tableName MODIFY
columnName TIMESTAMP(precisionVal)".
I was surprised to find that indexes
on these columns did NOT have to be
rebuilt.
Again, this only makes sense if you're committed to Hibernate.
According to Oracle JDBC FAQ:
"11.1 drivers by default convert SQL DATE to Timestamp when reading from the database"
So this is an expected behaviour.
To me this means that actual values coming from DATE columns are converted to java.sql.Timestamp, not that bind variables with java.util.Date are converted to java.sql.Timestamp.
An EXPLAIN PLAN output would help identifying the issue. Also, an Oracle trace could tell you exactly what type is assigned to the bind variable in the query.
If that's really happening it could be a Oracle bug.
You can work around it this way:
Create an FBI (Function Based Index) on the DATE column, casting it to a TIMESTAMP. For example:
CREATE INDEX tab_idx ON tab (CAST(date_col AS TIMESTAMP)) COMPUTE STATISTICS;
Create a View that contains the same CAST expression. You can keep the same column name if you want:
CREATE VIEW v AS
SELECT CAST(date_col AS TIMESTAMP) AS date_col, col_1, ... FROM tab;
Use the View instead of the Table (it's often a good idea anyway, e.g. if you were already using a View, you wouldn't need to change the code at all). When a java.sql.Timestamp variable will be used with date_col in the WHERE condition, (if enough selective) the Index will be used.
If you find out why there was a java.sql.Timestamp (or Oracle fixes the potential bug), you can always go back just changing the View (and dropping the FBI), and it would be completely transparent to the code

Compare time part of datetime field in Hibernate

I've got an application that uses a hibernate(annotations)/mysql combination for ORM. In that application, I got an entity with a Date field. I'm looking for a way to select on that date within a time range (so hh:mm:ss without the date part).
In MySQL there's a function TIME(expression) that can extract the time part and use that in the where clause, but that does not seem to be available in Hibernate without switching to native queries. Is there an option in hibernate to do this, or should I loop through the results in java and do the comparison there? Would this be much slower as the MySQL solution, since that would not use indexes anyway?
The following functions are available in HQL, maybe you could use them:
second(...), minute(...), hour(...), day(...), month(...), year(...)
Add the expression as a SQL restriction rather than having a full native query. I don't know MySQL specifically, but imagine something like this:
Criteria criteria = session.createCriteria(MyTable.class);
criteria.add(
Expression.sql(
"TIME( {alias}.my_date, 'hh:mm:ss') >= :1",
dateRangeMin,
new StringType()
)
);

Categories