Storing milliseconds in MySQL timestamp column using Hibernate - mysql

I'm trying to store a java Date with milliseconds in MySQL's timestamp column using Hibernate, but the millisecods are stored always as .000.
The definition of the column in hibernate is as follows:
#Type(type="timestamp")``
private Timestamp timestamp;
In DB the column is declared as TIMESTAMP(3)
I've tried different combinations, including Date, but neither helped.
I use MySQL 5.6.25, Connector/J version 5.1.37, Hibernate 4.0.1.
I've been investigating it for a while, but still couldn't find any solution that works form me.
ANy help will be appreciated.

Have you tried using DATETIME(3) or TIMESTAMP(4)? I believe both of these will give you the milliseconds. If you are trying to get the millisecond time where the interatction happens such as the row becomes updated you can use ON UPDATE DATETIME(3)

It seems that Hibernate and MySql interaction removes de milliseconds precision of your Date/Timestamp Java property. I have the same problem.
My solution is to "hack" hibernate telling that the entity property is an string and then serializing/deserializing the value in the setter/getter
#Column(name="time")
private String time
...
public Date getTime(){
return strTodate( this.time );
}
public void setTime(Date value){
this.time = dateToStr( value );
}
When MySQL receives an String for a Datetime(3) column, string is properly converted and milliseconds are not lost :-)
Fortunately, when reading from MySQL, Datetime(3) is propery serialized to string without milliseconds lost
The string date format used is "yyyy-MM-dd hh:mm:ss.SSS"
The idea of this solution is, don't let hibernate deal with dates. Delegate the responsability to MySql.
Unfortunatelly, MySQL doesn't accept an standard ISO string (i.e.: "yyyy-MM-ddThh:mm:ss.SSSZ") and this solution is not compatible with postgres (string date format is not the same)

Related

MySQL/Hibernate: getting value +1H on insert type Time

I'm working on a java web project that uses:
Hibernate 5.2.2/JPA 2.0+ MySQL5InnoDBDialect
MySQL 5.6.15-innoDB (on EasyPHP/PHPMyAdmin) + JDBC connector 6.0.4
Joda time API 2.9.4 + Fasterxml jackson API 2.8.3
I'm facing a problem on inserting Time data on database. everytime i put a row, i get a +1H value on time column!
Attribute on Java:
#JsonFormat(shape=JsonFormat.Shape.STRING, pattern="HH:mm")
#Column(name = "RES_DUREE", nullable = false)
#Temporal(TemporalType.TIME) private Date resDuree;
Attribute on SQL:
RES_DUREE TIME NOT NULL;
EDIT (After Adrian Shum's Comment):
Connection line:
jdbc.url =
jdbc:mysql://localhost:3306/mydb?useUnicode=true&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=UTC
I do use UTC but it still 1H+ .
Any suggestion will help, thanks.
Normally it is caused by server and DB time zone mismatch.
In brief, java.util.Date does not contain Timezone information. Conceptually you can treat it as simply representing Date + Time (similar to what JODA/JSR310 LocalDateTime is doing). Therefore if your app server is UTC+10, and your DB is UTC+2, when you save a date of "2016-10-13 10:11:12", although your app server is treating it as "2016-10-13 10:11:12 +10:00", it is simply passing "2016-10-13 10:11:12" to DB. Given DB is UTC+2, it is thinking the time actually means "2016-10-13 10:11:12 +02:00". Situation become more messy if your JDBC connection is claimed to be "UTC+10", most DB is going to "smartly" translate "2016-10-13 10:11:12 +02:00" to "2016-10-13 18:11:12 +10:00" which caused weird time stored and retrieved.
You may diagnose by tracing the SQL (and the actual value used) for corresponding inserts and select. You should see discrepancies between the values, vs the value stored in table. Such tracing can be done by misc way, e.g.
Older version of Hibernate can show the parameter used in prepared statement by turning on proper logger
You may use tools like JdbcDsLog (Disclaimer: I am maintainer for a fork of JbdcDsLog at http://github.com/adrianshum/jdbcdslog)
There is probably tools in DBMS side to trace incoming queries.
Best way to solve is to make sure everything is in the same timezone, and the most rational choice for timezone is UTC.

Querying Unix Timestamp using JOOQ

I'm using a MySQL server, and I'm trying to retrieve a timestamp using the JOOQ API. However, I think the default JOOQ behavior returns the time in the local timezone, and not UTC (it's not using UNIX_TIMESTAMP(). Using JOOQ, my query looks like
db.select(USER_TABLE.REGISTERED_ON)
.from(USER_TABLE)
.where(USER_TABLE.EMAIL.equal(email)
.fetchAny()
However, the sql that I want to execute should be something like this
SELECT UNIX_TIMESTAMP(schema.table.timestamp_col)
FROM schema.table
WHERE email="someone#domain.com"
Is it possible using the JOOQ API? If not, what's the best way to run this query, because I really want to be able to use the generated code (USER_TABLE, USER_TABLE.REGISTERED_ON, etc).
EDIT: I'm now doing the following, but is it safe? Basically I'm removing the quotations from JOOQ's generated classes.
String timestamp_field = USER_TABLE.REGISTERED_ON.toString().replace("\"", "");
Field<?> f = DSL.field("UNIX_TIMESTAMP(" + timestamp_field + ")");
Record r = db.select(f)
.from(USER_TABLE)
.where(USER_TABLE.EMAIL.equal(email))
.fetchAny();
There are several ways to tackle this problem:
Use a Converter or Binding to convert the type
You can register a data type Converter or Binding in the source code generator. This way, the code generator will generate a Field<YourType> for every TIMESTAMP field in the database. Possible useful types are:
java.lang.Long
java.time.Instant
java.time.OffsetDateTime (note that jOOQ 3.7 will support this via [#4338])3
Use plain SQL
Use plain SQL every time you want to do an explicit conversion using the UNIX_TIMESTAMP() function. Your solution works:
String timestamp_field = USER_TABLE.REGISTERED_ON.toString().replace("\"", "");
Field<?> f = DSL.field("UNIX_TIMESTAMP(" + timestamp_field + ")");
But it is not recommended because:
You should never rely on any toString() implementation of any Java type.
You should generally try to avoid string concatenation with jOOQ's plain SQL API
A better solution would be:
DSL.field("UNIX_TIMESTAMP({0})", Long.class, USER_TABLE.REGISTERED_ON);
Or even:
public static Field<Long> unixTimestamp(Field<Timestamp> arg) {
return DSL.field("UNIX_TIMESTAMP({0})", Long.class, arg);
}
Use BIGINT in the database
You could of course use BIGINT or BIGINT UNSIGNED in the database instead of TIMESTAMP. This way, you will always automatically have the integer unix timestamp value. This is just a workaround for completeness's sake.

How do i map a sql Time type to a java type using hibernate?

I've got a sql data type of TIME. Now, the data for 12:00 AM is stored as 00:00:00.
In the POJO that is mapped to the table, I have given the datatype as java.util.Date as per the hibernate specification. However, on loading a row that has the data type 00:00:00 i get this exception.
Cannot convert value '00:00:00' from column 18 to TIMESTAMP
How do I solve this?
For SQLServer you can use something like this. It should also work for time types I guess.(I don't know if it changes for other DB types)
private Date lockedUntil;
#Column(name="DATE", columnDefinition="DATETIME")
#Temporal(TemporalType.TIMESTAMP)
public Date getDate() {
return date;
}

MySQL - Passing UTC timestamps to sprocs via JDBC

I have a MySQL Server set to UTC (##global.time_zone = '+00:00') and a table with a DATETIME column in which I store dates in UTC. I'm having problems getting UTC dates to come through when I call a stored procedure via JDBC. Example:
java.util.Date now = new java.util.Date();
sproc = conn.prepareCall("{call TzTestInsert(?)}");
sproc.setTimestamp(1, new java.sql.Timestamp(now.getTime()), Calendar.getInstance(TimeZone.getTimeZone("GMT+00:00")));
sproc.execute();
The TzTestInsert sproc simply takes a DATETIME and inserts it into the table.
I'd expect the database to now hold my current time in UTC, but in fact it holds the current time for my timezone.
If I change the sproc to take a string it works...
DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
...
sproc.setString(1, dateFormat.format(now));
but I'd rather use the correct type in the sproc.
Also works if I bypass the sproc, but again not my preferred solution...
String sql = "INSERT INTO TzTest VALUES('" + dateFormat.format(now) + "') ;
With the original sproc I have the same issue if I use a TIMESTAMP datatype in the sproc and table, which isn't surprising with the server in UTC since any timezone conversions specific to MySQL TIMESTAMP should be noops.
Calling the sproc from a MySQL Workbench connection works fine, e.g.
CALL TzTestInsert(UTC_TIMESTAMP());
Seems like the problem is in JDBC. I've looked at the various timezone connection parameters and haven't found any that make a difference.
I must be missing something basic - lots of people do this, right?
Solution was to pass the JDBC driver "useLegacyDatetimeCode=false". See mysql bug http://bugs.mysql.com/bug.php?id=15604
Looks like they left the old code in the driver for backwards compatibility.

BLToolKit: how to fetch 'empty' datetime field?

Request Execute is failed if one of fields to be mapped has DateTime field and corresponding value in DB has '0000-00-00' or '0001-01-01'. The following error is returned
Unable to convert MySQL date/time value to System.DateTime
Is there any possibility to fetch such value?
I've tried to specify the 'DateTime?' value as property type - it doesn't help too (actually, I didn't expect that to be helpful).
P.S. I use MySql 5.1
I came across a similar problem using NHibernate with the same error in an exception.
It's due to MySQL's unique "feature" of allowing invalid dates in a DATE field, especially using 0000-00-00 as a default value for DATE NOT NULL columns. When such a date is encountered, it throws an exception when converting itself to a DateTime.
The suggested solution for this was to add
Allow Zero Datetime=True;
to the connection string, however in practice this did not work for me. I eventually solved the problem by altering the connection string adding
Convert Zero DateTime=true;
so your app.config section would look something like this
<connectionStrings>
<add
name="ConnectionString.MySql"
connectionString="Server=localhost;Port=3306;Database=BLT;Uid=someuser;Convert Zero DateTime=true;"
providerName="MySql.Data.MySqlClient"/>
Have you tried the MapValue attribute? I'm not sure if this will work but...
[MapValue(null, "0000-00-00")]
[MapValue(null, "0001-01-01")]
public DateTime? theDate;
i think you have to control it by another property.
[MapField("the_date")]
public DateTime? theDate; // Map
[MapIgnore]
public DateTime theDateControl
{
set {
if(theDate.HasValue)
{
....
}
}
}