EclipseLink DDL creating MySql DATETIME with fractional seconds - java

I am using EclipseLink 2.6.0 in my project together with MySQL 5.6.19.
Since mysql 5.6.4 supports a fieldtype DATETIME(6) which allows to store a date with milliseconds precision in its value. Also EclipseLink 2.6.0 says it supports this functionality.
I am creating a database from my entities. And I am not able to force it to create a proper field. In logs, during database creation I constantly see:
CREATE TABLE MY_TABLE (..., DATE_FIELD DATETIME ...)
when, obviously, what I want is:
CREATE TABLE MY_TABLE (..., DATE_FIELD DATETIME(6), ...)
I tried using both, simple and annotated version:
private java.util.Date date1;
#Temporal(value = TemporalType.TIMESTAMP)
private java.util.Date date2;
but the outcome is always the same. So how does the Eclipselink supports this? How to determine the proper field type?

Thanks specializt for the tip, easy solution:
#Column(length=6)
private Date myTime;
works also with YodaTime converter (description)
#Column(length=6)
#Converter(name = "dateTimeConverter", converterClass = pl.ds.eemediation.storage.entities.converters.JodaDateTimeConverter.class)
#Convert("dateTimeConverter")
private DateTime date;

Related

JPA UTC timezone query predicate issue

I am having an issue upgrading Spring Boot Starter Parent from 2.0.9.RELEASE to 2.3.3.RELEASE. To be more specific, I've traced the issue down to migrating from 2.0.9.RELEASE to 2.1.0.RELEASE. Prior to 2.1.0.RELEASE all of our criteria builder predicates that were doing an equals comparison like the below example were working. With the upgrade to 2.1.0.RELEASE we are no longer able to do equals comparisons as no matching row is returned.
Predicate foreverDatePredicate = criteriaBuilder.equal(root.get("deactivatedAt"), LocalDateTime.of(9999, Month.DECEMBER, 31, 0, 0, 0))
It does appear that with these two Spring Boot releases they went from Hibernate Core 5.2.18.FINAL to 5.3.7.FINAL and possibly JPA 2.1 to 2.2. I'm not sure if these version changes are related to the below problem.
The issue appears to be happening because of a timezone mismatch between what is being used as the timezone locally (PST) and the timezone that is being stored in the database (UTC). In my sql editor I was able to verify the hibernate query works just fine and returns the appropriate rows when a timezone is not supplied in the where clause. All of our datetime columns are being stored in our MySQL database in UTC time. I tried using the following timezone related properties in application.properties and our Hibernate JPA properties but both appeared to either not work or just be completely ignored:
# application.properties
spring.jpa.properties.hibernate.jdbc.time_zone=UTC
# Used in jpaProperties property under entityManagerFactory
hibernate.jdbc.time_zone=UTC
As some other threads have suggested I can set the JVM timezone to UTC, and after trying it this works; however, I would rather avoid doing this as it seems like a pretty far-reaching fix for something that should be able to be resolved at a more granular level:
#PostConstruct
public void started() {
TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
}
I eventually converted our Hibernate entity types that are responsible for mapping to the MySQL datetime columns from LocalDateTime to OffsetDateTime as shown below. OffsetDateTime stores the full timestamp with timezone in UTC time by default and this has resolved the issue and our datetime equals predicates are working again but this has caused a major refactoring headache.
Changed from
#MappedSuperclass
public class BitemporalEntity implements Serializable {
private static final long serialVersionUID = 2966895098986638738L;
#Column(name = "deactivated_at")
protected LocalDateTime deactivatedAt;
To
#MappedSuperclass
public class BitemporalEntity implements Serializable {
private static final long serialVersionUID = 2966895098986638738L;
#Column(name = "deactivated_at")
protected OffsetDateTime deactivatedAt;
I'm just wondering if I'm missing something here. Is using OffsetDateTime instead of LocalDateTime a good resolution to the issue or is there another way?

Hibernate not correctly storing ZonedDateTime as datetimeoffset in SQL Server

I want to use a datetimeoffset column in SQL Server 2008 that stores both the date & time plus the timezone.
The DDL definition of the column in the database:
LastUpdatedDateTime datetimeoffset DEFAULT SysDateTimeOffset()
The Java definition of the field on my entity mapped using Hibernate:
private ZonedDateTime lastUpdatedDateTime = ZonedDateTime.now();
I'm using Hibernate 5.1.0.Final (via Spring boot 1.3.2.RELEASE) and including org.hibernate:hibernate-java8.
Observed behavior (by querying database using WinSQL):
Insert data via a SQL insert statement results in storing the correct date&time and correct timezone: 2016-03-03 13:41:17.5358944 -07:00
Insert data via saving Java entity (with field initialized as per Java code fragment above).
Java reports the date/time value (before save) as: 2016-03-04T14:18:17.076-07:00[America/Denver]
After the save, WinSQL reports the value stored in the database as: 2016-03-04 14:18:17.0760000 +00:00
This has the same date&time, but the wrong timezone (UTC rather than -07:00).
When I declared the field in Java using Timestamp instead of ZonedDateTime, I got the same behavior.
How do I get the timezone to be correctly stored? I don't really care if it is stored as UTC or -07:00 time zone as long as the time is correct based on the time zone. I would think that Hibernate would provide support for this (in the hibernate-java8 library) and that I wouldn't have to code a custom converter or custom user data type.
I finally found a solution:
Use java.time.OffsetDateTime instead of ZonedDateTime for the entity field. As per the class Javadoc, OffsetDateTime is intended for use in database persistence.
Revert back to Hibernate 4 (due to other issues I was having). So I don't know if the next steps are necessary if you are using Hibernate 5 with the hibernate-java8 library.
Add a Hibernate Converter from OffsetDateTime to String. Apparently the datetimeoffset column is being treated by default by JDBC as a String (and not a microsoft.sql.DateTimeOffset class as suggested by the Microsoft JDBC driver documentation). The logic in this converter had to deal with the complication that SQL Server only stores 7 digits for nanoseconds while OffsetDateTime provides 9.
Ensure the Converter is included in the Hibernate EntityManager.
The details on these steps are as follows:
DDL column definition is unchanged.
Entity field definition:
private OffsetDateTime lastUpdatedDateTime;
Converter class:
#Converter(autoApply = true)
public class OffsetDateTimeConverter implements AttributeConverter<OffsetDateTime, String> {
private static DateTimeFormatter FORMATTER_FROM_DB = DateTimeFormatter.ofPattern(
"yyyy-MM-dd HH:mm:ss.nnnnnnn xxx");
private static DateTimeFormatter FORMATTER_TO_DB = DateTimeFormatter.ofPattern(
"yyyy-MM-dd HH:mm:ss.nnnnnnnnn xxx");
#Override
public String convertToDatabaseColumn(OffsetDateTime attribute) {
if (attribute == null) {
return null;
}
return attribute.format(FORMATTER_TO_DB);
}
#Override
public OffsetDateTime convertToEntityAttribute(String dbData) {
if (dbData == null) {
return null;
}
return OffsetDateTime.parse(dbData, FORMATTER_FROM_DB);
}
}

Hibernate Search MultiFieldQueryParser with a Date property

I upgraded from Hibernate Search 4.4 to 5.3 and sucessfully migrated all issues in Hibernate Search Migration Guides, but I'm encountering the following error regarding a date property when using MultiFieldQueryParser.
org.hibernate.search.exception.SearchException: HSEARCH000233: The specified query '+(dateField:value)' contains a string based sub query which targets the numeric encoded field(s) 'myDate'. Check your query or try limiting the targeted entities.
My date property is pretty standard
#Column(name = "my_date")
#Temporal(TemporalType.TIMESTAMP)
#Field
#DateBridge(resolution = org.hibernate.search.annotations.Resolution.DAY)
private Date myDate;
Versions
Hibernate 4.3
Hibernate Search 5.3
had to explicitly import (maven) org.apache.lucene.lucene-queryparser (4.10.4) for org.apache.lucene.queryparser.classic.MultiFieldQueryParser, otherwise it wouldn't find it
Also found this issue (HSEARCH-1870) regarding date values, not sure if related.
Am I missing something on myDate declaration?
EDIT: missed one migration requirement - see my answer bellow.
As it turns out I missed one migration requirement: enconding is set to numeric in HS 5 by default.
Numeric Field(s) being used by default
Numbers and Dates now indexed as Numeric Field by default
Setting the encoding to mimic the behavior in previous versions fixed it.
#Column(name = "my_date")
#Temporal(TemporalType.TIMESTAMP)
#Field
#DateBridge(resolution = org.hibernate.search.annotations.Resolution.DAY, encoding = EncodingType.STRING)
private Date myDate;

MySQL DATETIME precision (joda-time, Hibernate, org.jadira.usertype, hbm2ddl)

In my hibernate-4 entity, I am mapping a joda-time DateTime property using the recommended jadira usertypes:
#Entity
#Table(name="timing")
public class TimingEntity {
...
#Basic(optional=false)
#Column(name="moment")
#Type(type="org.jadira.usertype.dateandtime.joda.PersistentDateTime")
public DateTime getMoment() {
...
My database is MySQL. With hibernate property hbm2ddl.auto set to create value, I have the following column generated in my timing table:
CREATE TABLE `timing` (
...
`moment` DATETIME NOT NULL,
...
)
The generated CREATE TABLE contains the DATETIME column. The DATETIME in MySQL has only seconds precision, without fractional part. In order to enable fractional part, up to microseconds, MySQL 5.6.4 and higher enables DATETIME(precision) columns, for example DATETIME(3) to have milliseconds precision.
My question is -- is there way to specify precision for my temporal fields generated with hbm2ddl? At least, is this a matter of jadira usertypes, or java.sql, or jdbc driver machinery?
P.S. When I manually modify the DB table to have the exact column precision I want, say, DATETIME(3), everything works OK - joda DateTimes are written and read from the DB with milliseconds precision.
I've found one more solution that allows not to hardcode MySQL column definition snippet in your #Column annotation. Define your own hibernate dialect by overriding org.hibernate.dialect.MySQLDialect:
package org.yourproject;
import java.sql.Types;
import org.hibernate.dialect.MySQL5Dialect;
public class MySQL564PlusDialect extends MySQL5Dialect {
public MySQL564PlusDialect() {
super();
registerColumnType( Types.TIMESTAMP, 6, "datetime($l)" );
}
}
and specify it as hibernate property hibernate.dialect=org.yourproject.MySQL564PlusDialect (the dialect you'll want to extend may vary, e.g. org.hibernate.dialect.MySQL5InnoDBDialect instead).
Now you can adjust precision of your DATETIME from within #Column annotation by using length attribute:
#Basic(optional=false)
#Column(name="moment", length=3)
#Type(type="org.jadira.usertype.dateandtime.joda.PersistentDateTime")
public DateTime getMoment() {
...
which will produce DATETIME(3) column definition meaning milliseconds precision. If you need the simple DATETIME (no fractional seconds), just don't specify length. You can use value of length up to 6 which would mean microseconds precision.
If you happen to use a dialect different from the above one (for example the standard org.hibernate.dialect.MySQLDialect or maybe some other database), that will not break your code: the length attribute on #Column will be ignored.
P.S. It would be more sensible to exploit the precision attribute of #Column instead of length, but simple replacing of "datetime($l)" pattern with "datetime($p)" one in my own dialect implementation does not work offhand.
Use can use #Column#columnDefinition property for it
#Basic(optional=false)
#Column(name="moment" columnDefinition="DATETIME(3) NOT NULL")
#Type(type="org.jadira.usertype.dateandtime.joda.PersistentDateTime")
public DateTime getMoment()
{
...
Also you can try #Column#precision property, but in documentation is written that this working only for decimals.

Hibernate "ON UPDATE CURRENT TIMESTAMP" column

In MySQL it is possible to make a TIMESTAMP row being updated on every update operation. Is there a way to realize this for a column with Hibernate and map it to a POJO property?
So that I have something like this:
#Column
private Date updated;
If you want to do it at the database side, you can specify a custom column definition (if schema is generated by Hibernate, otherwise you need to declare it in your schema as desired), and instruct Hibernate that this property is generated at the database side:
#Column(columnDefinition = "TIMESTAMP ON UPDATE CURRENT TIMESTAMP")
#Generated(GenerationTime.ALWAYS)
private Date updated;
Alternatively, you can do it at the application side, as suggested by Jigar Joshi.
You can have this to accomplish this thing
#PreUpdate
protected void onUpdate() {
updated = new Date();
}

Categories