I've switched to Java 8 and Hibernate 5 to overcome the problem of not being able to store milliseconds in Hibernate.
private LocalDateTime date = LocalDateTime.now();
public LocalDateTime getDate() {
return date;
}
Maven dependencies
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>5.0.1.Final</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-java8</artifactId>
<version>5.0.1.Final</version>
</dependency>
Despite this Hibernate is still storing the LocalDateTime object as tinyblob. What am I missing? I'm using MySQL 5.6.19
According to this
Hibernate Issue, the hibernate-java8 module should map this to TIMESTAMP. I've tried placing a #Column(columnDefinition="TIMESTAMP) on the getter, but this led to a DataIntegrityViolationException.
Placed #Type(type="org.hibernate.type.LocalDateTimeType") on the getter. Database table still persisting as tinyblob.
As of this comment:
Hibernate is at version 5.2.10.Final
hibernate-java8 is deprecated and all functionality is included in hibernate-core
Hibernate provides type converters for all the JDK date/time types:
OffsetDateTime getters on entities should be annotated with #Type(type= "org.hibernate.type.OffsetDateTimeType")
LocalDateTime getters on entities should be annotated with #Type(type= "org.hibernate.type.LocalDateTimeType")
...etc.
Mysql's timestamp does not have millisecond precision so it wouldn't be able to store it as a timestamp, and probably detects this and saves it as a blob.
The reason it stores it as a TINYBLOB is that JPA 2.1 was released before Java 8 and the Date and Time API didn’t exist at that point in time.
To overcome this, you will need to define the mapping to java.sql.Date or java.sql.Timestamp yourself.
Related
Spring Boot 3 (Hibernate 6.1) in combination with PostgreSQL seems to have a problem with Instant values mapped to SQL timestamps (without time zone), as the following test case fails. Values read differ from the written values by the amount of the local time zone offset.
The test case executes successfully when using the H2 test database or when switching back to Spring Boot 2.7.6 (Hibernate 5.6).
JPA entity:
#Entity
public class MyEntity {
#Id
UUID id = UUID.randomUUID();
//#JdbcType(TimestampJdbcType.class) //work-around to get it somehow working
//(alternative: declare this globally in a custom Hibernate dialect)
//but why is this neccessary only with spring-boot-3 and postgres?
#Column(columnDefinition = "timestamp")
Instant createdTimestamp = Instant.now();
}
Spring Data repository:
public interface MyEntityRepository extends CrudRepository<MyEntity, UUID> {
}
JUnit integration test:
#DataJpaTest
#AutoConfigureTestDatabase(replace = Replace.NONE) //comment out to use the H2 database
class MyEntityRepositoryITest {
#Autowired
MyEntityRepository myEntityRepository;
#Test
#Transactional(propagation = NOT_SUPPORTED) //do not use a transaction
void readInstantSameAsPersisted() {
var entity = new MyEntity();
myEntityRepository.save(entity);
var read = myEntityRepository.findById(entity.id).orElseThrow();
assertEquals(entity.createdTimestamp.getEpochSecond(), read.createdTimestamp.getEpochSecond());
}
}
Maven pom.xml:
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>test</scope>
</dependency>
Spring application.properties:
spring.jpa.hibernate.ddl-auto=create
spring.datasource.url=jdbc:postgresql://localhost:5432/dbname
spring.datasource.username=username
spring.datasource.password=password
Notes:
Setting spring.jpa.properties.hibernate.jdbc.time_zone to any value doesn't help.
I don't see switching to LocalDateTime as an option, as I want the createdTimestamp to represent an unambiguous point in time.
I don't see switching to a SQL type with zone information as an option, as I don't want to allow values with varying zones in the database.
spring.datasource.hikari.connection-init-sql=SET TIME ZONE 'UTC' also works as a work-around, but I don't see why this is neccessary -- reading a value that is based on seconds since 1970-01-01 should give the same result as the written value, regardless of which zone is used behind the scenes.
pgJDBC does not support Instant, but Hibernate does.
This answer could be related, but then why does it work with the older Hibernate?
Update
This answer says timestamp with time zone does (unlike the name suggests) in fact not carry additional zone information and just "stores a point on the UTC time line" that can be represented in different zones, which seems like a better fit for my use case.
The PostgreSQL documentation says:
For timestamp with time zone, the internally stored value is always in UTC... An input value that has an explicit time zone specified is converted to UTC using the appropriate offset for that time zone.
If you declare the field as timestamp with time zone, or briefly as timestamptz, then you will reach exactly what you want. It will also fit your requirement not to have multiple timezones in the database.
I am getting an SQL exception while am passing DateTime to my Insert query via Hibernate.
Please find my Java code where am trying to pass DateTime:
claimGroupingHistory.setCreatedAt(new DateTime());
claimGroupMappingRepository.insertClaimGroupingHistory(claimGroupingHistory.getDealerCode(),
claimGroupingHistory.getCreatedAt(),
claimGroupingHistory.getCreatedBy());
I am getting DateTime in the format when I sysout: 2019-01-10T13:59:36.700+05:30.
Please find my insert query
Please find my exception am getting
2019-01-10 13:59:36,754 [http-9292-1] ERROR org.hibernate.engine.jdbc.spi.SqlExceptionHelper: 146 - Data truncation: Incorrect datetime value: '\xAC\xED\x00\x05sr\x00\x16org.joda.time.DateTime\xB8<xdj[\xDD\xF9\x02\x00\x00xr\x00\x1Forg.joda.time.base.BaseDateTime\xFF\xFF\x' for column 'created_at' at row 1
2019-01-10 13:59:36,774 [http-9292-1] ERROR com.cat.pscs.api.controller.BaseController: 57 - Data Integrity Violation Exception : org.springframework.dao.DataIntegrityViolationException: could not execute statement; SQL [n/a]; nested exception is org.hibernate.exception.DataException: could not execute statement
To help hibernate persist DateTime to a database, you should use joda-time-hibernate rather than joda-time in the project.
<dependency>
<groupId>joda-time</groupId>
<artifactId>joda-time-hibernate</artifactId>
<version>1.4</version>
</dependency>
Modify your entity:
#Column
#Type(type="org.joda.time.contrib.hibernate.PersistentDateTime")
private DateTime createdAt;
Note that version 1.4 is for Hibernate 3.6. So update joda-time-hibernate when you use a higher level hibernate.
Alternatives:
Declare an extra dependency in the pom.xml, it can support the persistence for joda-time.
<dependency>
<groupId>org.jadira.usertype</groupId>
<artifactId>usertype.extended</artifactId>
<version>5.0.0.GA</version>
</dependency>
Modify the entity:
#Column
#Type(type="org.jadira.usertype.dateandtime.joda.PersistentLocalDate")
private DateTime createdAt;
You should use the following annotation in your entity class in order to persist joda DateTime:
#Type(type = "org.jadira.usertype.dateandtime.joda.PersistentDateTime")
private DateTime createdAt;
Also make sure that your have jadira-usertype-core.jar in your classpath:
<dependency>
<groupId>org.jadira.usertype</groupId>
<artifactId>usertype.core</artifactId>
<version>3.1.0.CR1</version>
</dependency>
I am using Java 8 LocalDateTime class to persist date in the datastore.
The date is this format 2017-07-24T01:00:00.000 is persisted as an EmbeddedEntity.
private LocalDateTime matchDateTime;
The persistence flow is alright. But when I load the entity exception is thrown.
com.googlecode.objectify.LoadException: Error loading : java.time.LocalDateTime must have a no-arg constructor
at com.googlecode.objectify.impl.EntityMetadata.load(EntityMetadata.java:78) ~[objectify-5.1.21.jar:na]
at com.googlecode.objectify.impl.LoadEngine.load(LoadEngine.java:185) ~[objectify-5.1.21.jar:na]
Caused by: java.lang.IllegalStateException: java.time.LocalDateTime must have a no-arg constructor
at com.googlecode.objectify.impl.TypeUtils.getNoArgConstructor(TypeUtils.java:47) ~[objectify-5.1.21.jar:na]
at com.googlecode.objectify.ObjectifyFactory.construct(ObjectifyFactory.java:69) ~[objectify-5.1.21.jar:na]
Caused by: java.lang.NoSuchMethodException: java.time.LocalDateTime.<init>()
at java.lang.Class.getConstructor0(Class.java:3082) ~[na:1.8.0_131]
at java.lang.Class.getDeclaredConstructor(Class.java:2178) ~[na:1.8.0_131]
How do I fix this?
I researched & added in my pom.
<dependency>
<groupId>com.fasterxml.jackson.module</groupId>
<artifactId>jackson-module-parameter-names</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jdk8</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
<version>2.8.9</version>
</dependency>
Marked the date property with
#JsonSerialize(using = ToStringSerializer.class)
#JsonDeserialize(using = LocalDateTimeDeserializer.class)
private LocalDateTime matchDateTime;
but still it doesn't workout. Any guesses?
The current version of Objectify does not have builtin support for the J8 data types. However, you can add support in your app very easily.
Look in the Objectify source for the package com.googlecode.objectify.impl.translate.opt.joda. It contains the translators that allow Objectify to use joda time objects; you register these with the ObjectifyFactory before registering your entity classes. It should be apparent how they work; you just need to be able to convert between LocalDateTime and however you want it stored in the datastore.
A bit of warning: Do not be tempted to convert LocalDateTime into a java.util.Date, which represents an instant in time. Your LocalDateTime has no TZ and therefore is not an instant. It's best to represent this as the ISO 8601 string. Look at ReadablePartialTranslatorFactory... although the reflection is probably unnecessary for your use case.
I want to store the time what data type should I used in spring framework ?
Database which I used is MySQL
#Entity
public class ShopDetail {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String address;
private Double latitude;
private Double longitude;
private float rating;
private Time openingTime;
private Time closingTime;
}
If you're using Java 8, the most business logic, entity fields may be using Instant - basic class in java.time API for representing a moment in the timeline in UTC.
If you need timezone data, you may consider using:
ZonedDateTime is an immutable representation of a date-time with a time-zone. This
class stores all date and time fields
Another option LocalDateTime:
A date-time without a time-zone in the ISO-8601 calendar system, such
as 2007-12-03T10:15:30
But then you would rely on a default system timezone which may bring contradicting results if a system timezone would change.
Check Java 8: What's the difference between Instant and LocalDateTime? or Hibernate with Java 8 LocalDate & LocalDateTime in Database for explicit explanation.
you can use date time format.
http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/format/annotation/DateTimeFormat.html
Spring 3.2 Date time format
You can use Java 8 time types in your entities. Just add this dependencies to your projects:
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-java8</artifactId>
</dependency>
According to this article The 5 laws of API dates and times I'd recommend to use ZonedDateTime in REST projects.
Also pay attention to this topic...
I have a problem about the management of dates with milliseconds. I understand the need to use the TIMESTAMP to store milliseconds:
#Temporal(TIMESTAMP)
#Column(name="DATE_COLUMN", nullable = false)
#Override public java.util.Date getDate() { return this.date; }
But if I can't compare this date to another instance of java.util.Date, unless I pay attention to the order of equals() call, because this.date instance is a java.sql.Timestamp. How to get a java.util.Date from JPA ? Because the date that comes from JPA, even if the method signature is a java.util.Date is actually an instance of java.sql.Timestamp.
java.util.Date newDate = new Date(this.date.getTime());
this.date.equals(newDate) == false
newDate.equals(this.date) == true
I've try to modify my method in the persistence class:
#Override
public Date getDate() {
return this.date == null ? null : new Date(this.date.getTime());
}
It's working, but it's not efficient with lots of data.
There are other options :
I could modify the design of my persistence class, using #PostLoad in order to create a java.util.Date from the persited date after I retrieve it.
I wonder if I can not get a result using a ClassTransformer?
Have you ever been confronted with this problem? What I do not correctly? What is the best way to handle this problem?
TBH, I'm not sure of the exact status of this but there might indeed be a problem with the way Hibernate (which is your JPA provider, right?) handles TIMESTAMP columns.
To map a SQL TIMESTAMP to a java.util.Date, Hibernate uses the TimestampType which will actually assign a java.sql.Timestamp to your java.util.Date attribute. And while this is "legal", the problem is that Timestamp.equals(Object) is not symmetric (why on earth?!) and this breaks the semantics of Date.equals(Object).
As a consequence, you can't "blindly" use myDate.equals(someRealJavaUtilDate) if myDate is mapped to a SQL TIMESTAMP, which is of course not really acceptable.
But although this has been extensively discussed on the Hibernate forums, e.g. in this thread and this one (read all pages), it seems that Hibernate users and developers never agreed on the problem (see issues like HB-681) and I just don't understand why.
Maybe it's just me, maybe I just missing something simple for others, but the problem looks obvious to me and while I consider this stupid java.sql.Timestamp to be the culprit, I still think that Hibernate should shield users from this issue. I don't understand why Gavin didn't agree on this.
My suggestion would be to create a test case demonstrating the issue (should be pretty simple) and to report the problem (again) to see if you get more positive feedback from the current team.
Meanwhile, you could use a custom type to "fix" the problem yourself, using something like this (taken from the forum and pasted as is):
public class TimeMillisType extends org.hibernate.type.TimestampType {
public Date get(ResultSet rs, String name) throws SQLException {
Timestamp timestamp = rs.getTimestamp(name);
if (timestamp == null) return null;
return
new Date(timestamp.getTime()+timestamp.getNanos()/1000000);
}
}
java.sql.Timestamp overrides the compareTo(Date) method, so it should be no problem using compareTo(..)
In short - java.util.Date and java.sql.Timestamp are mutually comparable.
Furthermore, you can always compare the date.getTime(), rather than the objects themselves.
And even further - you can use a long field to store the date. Or even a DateTime (from joda-time)
In my experience you don't want the java.sql.Timestamp out into your logic - it creates a lot of strange errors just as you pointed out, and it does not get any better if your application does serialization.
If it works with the override that returns a new java.util.Date then go for that one. Or even better, go for JodaTime. You'll find lots of examples out on the net doing that. I would not worry about performance here as your database is in magnitude more slow than the creation of a new java.util.Date object.
EDIT:
I see that you are using Hibernate. If you use annotations you can do:
#Type(type = "org.joda.time.contrib.hibernate.PersistentDateTime")
public DateTime getProvisionByTime() {
return provisionByTime;
}
Then you will get nice DateTime objects from Jodatime in your persistent objects. If you want to only have a date, you can use LocalDate like this:
#Type(type = "org.joda.time.contrib.hibernate.PersistentLocalDate")
public LocalDate getCloudExpireDate() {
return cloudExpireDate;
}
IF you use maven, the following dependencies should get this set up for you (you might need to update the hibernate versions)
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate</artifactId>
<version>3.2.6.ga</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-annotations</artifactId>
<version>3.3.1.GA</version>
</dependency>
<dependency>
<groupId>joda-time</groupId>
<artifactId>joda-time-hibernate</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
<version>1.6.1</version>
</dependency>
The problem is critical for DAO tests:
Employer employer1 = new Employer();
employer1.setName("namenamenamenamenamename");
employer1.setRegistered(new Date(1111111111)); // <- Date field
entityManager.persist(employer1);
assertNotNull(employer1.getId());
entityManager.flush();
entityManager.clear();
Employer employer2 = entityManager.find(Employer.class, employer1.getId());
assertNotNull(employer2);
assertEquals(employer1, employer2); // <- works
assertEquals(employer2, employer1); // <- fails !!!
So the result is really surprising and writing tests became tricky.
But in the real business logic you will never use entity as a set/map key because it is huge and it is mutable. And you will never compare time values by equal comparison. And comparing whole entities should be avoided too.
The usual scenario uses immutable entity ID for map/set key and compares time values with compareTo() method or just using getTime() values.
But making tests is a pain so I implemented my own type handlers
http://pastebin.com/7TgtEd3x
http://pastebin.com/DMrxzUEV
And I have overridden the dialect I use:
package xxx;
import org.hibernate.dialect.HSQLDialect;
import org.hibernate.type.AdaptedImmutableType;
import xxx.DateTimestampType;
import java.util.Date;
public class CustomHSQLDialect extends HSQLDialect {
public CustomHSQLDialect() {
addTypeOverride(DateTimestampType.INSTANCE);
addTypeOverride(new AdaptedImmutableType<Date>(DateTimestampType.INSTANCE));
}
}
I haven't decided yet - would I use this approach both for tests and production or for tests only.
JPA should return a java.util.Date for an attribute of type java.util.Date, the #Temporal(TIMESTAMP) annotation should only affect how the date is stored. You should not get a java.sql.Timestamp back.
What JPA provider are you using? Have you tried this in EclipseLink, the JPA reference implementation?