Let say you have a class with a java.util.Date field. Maybe a To do class or an Event planner class.
#Temporal(TemporalType.TIMESTAMP)
private Date startTime;
This will map to a datetime column in MySQL.
There are use cases when you want to know precisely when this Event occurs. "What is scheduled for Aug 11, 2011 at 8 PM?"
Sometimes you just want to know which events are planned for a specific date. "What is scheduled for August 11, 2011?"
If your JPAQL is:
SELECT blah blah etc. WHERE ti.startTime = :d1
and the parameter is a java.util.Date instance:
query.setParameter("d1", date, TemporalType.DATE);
your results will be restricted by the date but also the time.
This is such a common use case that I'm surprised that there is no simple way to do this even in JPA 2.0.
I'd rather not use a vendor specific hack nor play around with strings/substrings.
How have you solved this problem?
I know I'm a little late, but I've found a way to do this.
I was using a jpql like this:
select s from S s where date(s.someDate) = :param
And I set the param with:
query.setParameter("param", param, TemporalType.DATE);
The way I've found to do this with Criteria Query was:
builder.equal(
builder.function("date", Date.class, root.get(S_.someDate)),
param
);
At least, with MySql it works.
I hope it can help.
select ... where ti.startTime >= :theDay and ti.startTime < :theNextDay
is a relatively easy solution to implement, and works on any JPA implementation.
Hibernate also allows adding functions to a dialect, in order to generate custom SQL.
Related
I would like to get millisecond precision in my MariaDB. After some research, I found that I needed to change the columnDefinition - so I did this in my entity:
#NotNull
#Column(name = "createdDate", columnDefinition = "DATETIME(3) NOT NULL")
#Temporal(TemporalType.TIMESTAMP)
private TimeStamp createdDate;
#PrePersist
void onPersist() {
createdDate = new Timestamp(new Date().getTime());
}
The resulting SQL to create the column is:
`createdDate` DATETIME(3) NOT NULL
Now, in the DB the value has indeed 3 decimals:
2016-09-12 16:57:44.000
... but they are always 000
What did I do wrong, or what did I forget ?
Edit: I tried without JAVA:
CREATE TABLE `test` (
`id` BIGINT(20) NOT NULL AUTO_INCREMENT,
`createdDate` DATETIME(3) NOT NULL,
PRIMARY KEY (`id`)
)
COLLATE='latin1_swedish_ci'
ENGINE=InnoDB
;
And then:
INSERT INTO test (createdDate)
VALUES(current_timestamp())
Result:
2016-09-13 13:57:44.000
I had the same problem with MariaDB and date types. I've tried org.joda.DateTime and java.util.time types. Both, the server and the client code supported milliseconds correctly.
The problem was that I was using MySQL Connector instead of MariaDB Connector/J JDBC driver.
Background
In most situations using MariaDB with MySQL Connector works out well, but I would never ever recommend this. When I was searching for the issue I was debugging through Hibernate and Connector code and saw many feature detections that were based on the server version number instead of a real feature detection. The version numbering of course differs between MySQL and MariaDB. So there's a big probability that there are far more compatibility issues that are quietly ignored.
Your problem most probably comes from the fact that you mix Dates and Timestamps. Changing the createdDate type to java.sql.Timestamp should solve your issue.
Also, if your version of MySQL is prior to 5.6.4, DateTime won't let you save time fractions.
EDIT after OP's edit :
you are still mixing the Date Java type with Timestamp when you do this :
createdDate = new Timestamp(new Date().getTime());
Can you try createdDate = new Timestamp(System.currentTimeInMilliseconds()); instead ?
Ideally you should use objects from a library like JodaTime to avoid such issues, but that's beyond the point of your question, just a tip :)
Ultimately, if this way of creating your Timestamp does not work, I would use the Timestamp type in DB instead of Datetime, but that's just trial and error as Datetime should work as well in your example.
Edit :
excerpt from Oracle's Date API :
Date()
Allocates a Date object and initializes it so that it represents the time at which it was allocated, measured to the nearest millisecond.
In which case using System.currentTimeInMilliseconds() shouldn't change the outcome - my bad.
To troubleshoot the problem, I'd start to create a date via SQL (without passing via Java objects) with CURRENT_TIMESTAMP to make sure that the field can indeed contain decimal time precision.. If it is OK, verify the value in the Java object with a debugger.. Might give you a lead. If both contain milliseconds, I'd look at the usage of the annotations or start from a working sample.
To do this using pure JPA :
#Column(name="STMP", columnDefinition = "TIMESTAMP (6)")
private Timestamp timestamp = Timestamp.from(Instant.now());
Hi I would like to generate a Predicate for spring data QueryDSLRepository that would generate following query (or equivalent):
SELECT * FROM USER user JOIN PASSWORD_POLICY policy
ON
user.password_policy_oid = policy.password_policy_oid
WHERE
user.password_expiration_date BETWEEN CURRENT TIMESTAMP - (SELECT EXPIRATION_WARN_PERIOD_IN_DAYS FROM PASSWORD_POLICY subQueryPolicy WHERE subQueryPolicy.password_policy_oid = policy.password_policy_oid) DAYS AND CURRENT TIMESTAMP
The meaning of this query would be:
Get me all users that password is about to expire
and by about to expire I mean - password expiration date is between now and (now - EXPIRATION_WARN_PERIOD_IN_DAYS from PASSWORD_POLICY table)
Is it possible?
This probably it can't be done by using only Spring Data predicate runner. AFAIK jpql does not support such datetime manipulation (add days etc). So what you can do if you still want use Querydsl is to use native JPASQLQuery. Unfortunately joining mapped entities is not easy, another drawback is that datetime manipulation capabilities are also not so nice in Querydsl. But I was able to manage your problem.
Assumptions:
User contains #ManyToOne PassworPolicy field, which is mapped by PASSWORD_POLICY_OID column.
DB2 datatabase
import static model.QPasswordPolicy.passwordPolicy;
import static model.QUser.user;
import static com.mysema.query.sql.SQLExpressions.datediff;
import static com.mysema.query.types.expr.DateTimeExpression.currentTimestamp;
...
NumberPath<Integer> userPasswordPolicyOidPath = new NumberPath<>(Integer.class, QUser.user, "PASSWORD_POLICY_OID");
QPasswordPolicy subQueryPolicy = new QPasswordPolicy("subQueryPolicy");
List<Integer> userIds =
new JPASQLQuery(entityManager, new DB2Templates())
.from(user)
.join(passwordPolicy).on(passwordPolicy.passwordPolicyOid.eq(userPasswordPolicyOidPath))
.where(datediff(DatePart.day, currentTimestamp(DateTime.class), user.passwordExpirationDate)
.lt(new JPASubQuery().from(subQueryPolicy)
.where(passwordPolicy.passwordPolicyOid.eq(subQueryPolicy.passwordPolicyOid))
.unique(passwordPolicy.expirationPeriodInDays)))
.list(user.userOid);
probably some one more condition expirationDate < currentTimeStamp is needed to satisfy the logic, but I will leave it to you :)
PS
userPasswordPolicyOidPath is ugly but I don't have idea how to get rid of this :(
One way to achieve it would be like this:
val now = DateTime.now
val today = now.toLocalDate
val tomorrow = today.plusDays(1)
val startOfToday = today.toDateTimeAtStartOfDay(now.getZone)
val startOfTomorrow = tomorrow.toDateTimeAtStartOfDay(now.getZone)
val todayLogItems = logItems.filter(logItem =>
logItem.MyDateTime >= startOfToday && logItem.MyDateTime < startOfTomorrow
).list
Is there any way to write the query in a more concise way? Something on the lines of:
logItems.filter(_.MyDateTime.toDate == DateTime.now.toDate).list
I'm asking this because in LINQ to NHibernate that is achievable (Fetching records by date with only day part comparison using nhibernate).
Unless the Slick joda mapper adds support for comparisons you are out of luck unless you add it yourself. For giving it a shot these may be helpful pointers:
* http://slick.typesafe.com/doc/2.0.0/userdefined.html
* http://slick.typesafe.com/doc/2.0.0/api/#scala.slick.lifted.ExtensionMethods
* https://github.com/slick/slick/blob/2.0.0/src/main/scala/scala/slick/lifted/ExtensionMethods.scala
I create a ticket to look into it in Slick at some point: https://github.com/slick/slick/issues/627
You're confusing matters by working with LocalDateTimes instead of using LocalDates directly:
val today = LocalDate.now
val todayLogItems = logItems.filter(_.MyDateTime.toLocalDate isEqual today)
UPDATE
A Major clarification is needed on the question here, Slick was only mentioned in passing, by way of a tag.
However... Slick is central to this question, which hinges on the fact that filter operation is actually into an SQL query by way of PlainColumnExtensionMethods
I'm not overly familiar with the library, but this must surely mean that you're restricted to just operations which can be executed in SQL. As this is a Column[DateTime] you must therefore compare it to another DateTime.
As for the LINQ example, it seems to recommend first fetching everything and then proceeding as per my example above (performing the comparison in Scala and not in SQL). This is an option, but I suspect you won't want the performance cost that it entails.
UPDATE 2 (just to clarify)
There is no answer.
There's no guarantee that your underlying database has the ability to do an equality check between dates and timestamps, slick therefore can't rely on such an ability existing.
You're stuck between a rock and a hard place. Either do the range check between timestamps as you already are, or pull everything from the query and filter it in Scala - with the heavy performance cost that this would likely involve.
FINAL UPDATE
To refer to the Linq/NHibernate question you referenced, here are a few quotes:
You can also use the date function from Criteria, via SqlFunction
It depends on the LINQ provider
I'm not sure if NHibernate LINQ provider supports...
So the answers there seem to be either:
Relying on NHibernate to push the date coercion logic into the DB, perhaps silently crippling performance (by fetching all records and filtering locally) if this is not possible
Relying on you to write custom SQL logic
The best-case scenario is that NHibernate could translate date/timestamp comparisons into timestamp range checks. Doing something like that is quite a deep question about how Slick (and slick-joda-mapper) can handle comparisons, the fact that you'd use it in a filter is incidental.
You'd need an extremely compelling use-case to write a feature like this yourself, given the risk for creating complicated bugs. You'd be better off:
splitting the column into separate date/time columns
adding the date as a calculated column (maybe in a view)
using custom SQL (or a stored proc) for the query
sticking with the range check
using a helper function
In the case of a helper:
def equalsDate(dt: LocalDate) = {
val start = dt.toDateTimeAtStartOfDay()
val end = dt.plusDays(1).toDateTimeAtStartOfDay()
(col: Column[DateTime]) => {
col >= start && col < end
}
}
val isToday = equalsDate(LocalDate.now)
val todayLogItems = logItems.filter(x => isToday(x.MyDateTime))
I have been doing queries in EF and everything working great but now i have in the db 2 fields that are actually CHAR.. They hold a date but in the form of a number, in SQL Management Studio i can do date1 >= date2 for example and i can also check to see if a number i have falls in between these 2 dates.
Its nothing unusual, but basically a field that represents a date (the number grows as the date does)...
Now in EF when i try to do >= it states you can't do this on a string, ok understand its c# so i tried doing Convert.ToDecimal(date1) but it gives me an error saying that its not supported.
I have no option of changing the db fields, they are set in stone :-(
the way i got it to work was request of details and do a .ToList and then use the .ToDecimal and it works but of course this is doing it in memory! and this defeats the object of EF i.e. for example adding to the query using iqueryable.
Another way i got it to work was to pass the SQL query to SqlQuery of the dbcontext but again i lose a lot of ef functionality.
Can anyone help?
I am really stuck
As you say that you tried >= I assume that it would work for you if you could do that in plain SQL. And that is possible by doing
String.Compare(date1, date2) >= 0
EF is smart enough to translate that into a >= operator.
The advantage is that you do not need to compare converted values, so indexes can be used in execution plans.
First of all, you can at least enable deferred execution of the query by using AsEnumerable() instead of ToList(). This won't change the fact that the database would need to return all the records when you do in fact execute the query, however.
To let the database perform the filtering, you need your query to be compatible with SQL. Since you can't do ToDecimal() in SQL, you need to work with strings directly by converting your myvar to a string that is in the same format as dateStart and dateEnd, then form your query.
i have trouble with select only month or year in JPA
in mysql i write statement follow:
select * from table where Month(date) = 12 ;
and in entity bean i write follow:
select t from table t where Month(t.date) = 12;
but it throw Error !!
PS: sorry i can't attach my stacktrace because i'm not at home :D
sorry but you cant do this with JPA as far as i know. hibernate's hql on the otherside knows stuff like month() hour() and minute().
check this question: JPA Date Arithmetic?
hope that helped
Not part of JPA, and consequently in the role of vendor extensions. Most JPA implementations support it ... and you don't say which one you're using. DataNucleus certainly supports MONTH, YEAR, DAY, HOUR, MINUTE, SECOND.
This might help someone.
Few JPA implementations have built-in support for date/time functions.
EclipseLink
EclipseLink supports EXTRACT allowing any database supported date/time
part value to be extracted from the date/time. The EXTRACT function is
database independent, but requires database support. EXTRACT(YEAR,
model.currencyExchangeDate), but it requires EclipseLink 2.4.x
FUNC allows for a database function to be call from JPQL. It allows calling any database functions not supported directly in JPQL.
To call the DB function MyFunc(a, b, c) use FUNC('MyFunc', a, b, c).
You can try FUNC('YEAR', currencyExchangeDate) to call 'YEAR'
function.
Hibernate
In HQL, you can have date function YEAR(date), MONTH(date), DAY(date)
to extract required details. Other time functions supported are
HOUR(date), MINUTE(date), SECOND(date).
Criteria API
CriteriaBuilder#function() : Create an expression for the execution
of a database function.
CriteriaQuery<IptExchangeratelines> cq = cb.createQuery(IptExchangeratelines.class);
Root<IptExchangeratelines> exRateLine = cq.from(IptExchangeratelines.class);
cq.where(cb.equal(cb.function("year", Integer.class,
exRateLine.get(exRateLine_.currencyExchangeDate)), year)
.and(cb.equal(cb.function("month", Integer.class,
exRateLine.get(exRateLine_.currencyExchangeDate)), month)));