Persist LocalDateTime, ZonedDateTime & Co with Hibernate


Take your skills to the next level!

The Persistence Hub is the place to be for every Java developer. It gives you access to all my premium video courses, monthly Java Persistence News, monthly coding problems, and regular expert sessions.


The Date and Time API, introduced in Java 8, finally replaced the old java.util.Date. It’s much easier to use and finally offers separate classes representing date, date with time, and only time information. That not only improves your business code but also makes it easier to use them as entity attributes. At least if you’re using the correct version of JPA and/or Hibernate.

You have 3 options if you want to use the right JDBC types when you persist classes of the Date and Time API:

  • If you’re using JPA in a version < 2.2 or Hibernate in a version < 5, You can implement a JPA AttributeConverter and convert the Java 8 class into one that is supported by Hibernate. I described this in detail in How to persist LocalDate and LocalDateTime with JPA. This approach does not use any Hibernate-specific APIs and is portable to other JPA implementations, but it is a little complicated.
  • You can use JPA in a version >= 2.2. It supports some of the classes of the Date and Time API as basic attribute types.
  • You can use the Hibernate-specific Java 8 support which was introduced with Hibernate 5. It supports a few more data types than JPA 2.2.

Java 8 Support in Hibernate >= 5

One of the features added with Hibernate 5 was the support of Java 8 classes like the Date and Time API.

Java 8 support in Hibernate 5.0 and 5.1

The Java 8 support was initially shipped in a separate jar file called hibernate-java8.jar, which you needed to add to the classpath of your application.

<dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-java8</artifactId>
    <version>5.1.0.Final</version>
</dependency>

Java 8 support in Hibernate >= 5.2

Since version 5.2, Hibernate ORM is based on Java 8 and the support for the Date and Time API has become part of the core distribution. The additional jar file is no longer required.

JDBC Mappings

Hibernate maps the classes of the Date and Time API to the corresponding JDBC types. The following table shows an overview of the supported classes and their JDBC mapping.

Java typeJDBC type
java.time.DurationBIGINT
java.time.InstantTIMESTAMP
java.time.LocalDateTimeTIMESTAMP
java.time.LocalDateDATE
java.time.LocalTimeTIME
java.time.OffsetDateTimeDepends on Hibernate version:
TIMESTAMP, TIMESTAMP_WITH_TIMEZONE, 2 columns
java.time.OffsetTimeTIME
java.time.ZonedDateTimeDepends on Hibernate version:
TIMESTAMP, TIMESTAMP_WITH_TIMEZONE, 2 columns

As you can see, the mapping of all types except OffsetDateTime and ZonedDateTime are straightforward. But the mapping of those 2 types is a little more complex. The reason is that not all databases support the column type TIMESTAMP_WITH_TIMEZONE.

I explained the different mapping options, the Hibernate versions in which they are available, and now to configure them in my guide to Hibernate’s TimezoneStorageType.

Here’s the short version:

  • Hibernate 5 normalizes your OffsetDateTime and ZonedDateTime to your application’s timezone and stores it without timezone information in a column of type TIMESTAMP.
  • Hibernate 6 introduced the @TimezoneStorageType annotation and a configuration parameter. They enable you to specify the handling of OffsetDate and ZonedDateTime. You can:
    • Store it in a column of type TIMESTAMP_WITH_TIMEZONE.
    • Normalize the timestamp to UTC and store it without timezone information in a column of type TIMESTAMP.
    • Normalize the timestamp to your application’s timezone and store it without timezone information in a column of type TIMESTAMP.
    • Store the timestamp without timezone information in a column of type TIMESTAMP and store the offset of the timestamp’s timezone to UTC in a separate column.

Date and Time API Classes As Entity Attributes

Hibernate supports the classes of the Date and Time API as BasicTypes. This provides the main advantage that you don’t have to add any additional annotations. Not even the @Temporal annotation which you currently add to each java.util.Date attribute.

Hibernate gets all required information from the type of the attribute. You can see an example of an entity with attributes of type LocalDate, LocalDateTime, and Duration in the following code snippet.

@Entity
public class MyEntity {
 
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;
     
    private LocalDate date;
     
    private LocalDateTime dateTime;
     
    private Duration duration;
     
    ...
}

You can then use these attributes in the same way as any other attributes in your Java code.

EntityManager em = emf.createEntityManager();
em.getTransaction().begin();
 
MyEntity e = new MyEntity();
e.setDate(LocalDate.now());
e.setDateTime(LocalDateTime.now());
e.setDuration(Duration.ofDays(2));
 
em.persist(e);

And as you can see in the following screenshot, Hibernate persists them with the right JDBC data type instead of the blob it uses without the hibernate-java8.jar.

Conclusion

Hibernate started supporting classes of the Date and Time API in version 5 and improved the support for ZonedDateTime and OffsetDateTime in version 6.

Hibernate 5.0 and Hibernate 5.1 were still based on Java 7 and required an additional jar file to support any Java 8 features. Since Hibernate 5.2, the Date and Time API support is part of the hibernate-core module.

Hibernate handles the classes of the Date and Time API as BasicTypes. This makes them even easier to use than the old java.util.Date. You no longer have to add any additional annotations to define the JDBC type to which you want to map it. Hibernate also supports a few more date types than required by the JPA specification.

When working with OffsetDateTime or ZonedDateTime, you should pay special attention to how Hibernate stores the timezone information. In version 5, Hibernate normalizes the timestamp to your application’s timezone and stores it without timezone information in the database. Since Hibernate ORM 6, you should use the @TimezoneStorageType annotation to specify the timezone handling.

13 Comments

  1. Avatar photo Arthur Eduardo Moura says:

    Hello, thank you!! Your post helped me a lot.

  2. could you please confirm below query’s
    1.hibernate 5.x support from which version of spring ?
    2.left joins, right join Query’s is it work with out relations?

    1. Avatar photo Thorben Janssen says:

      Hi,
      I don’t know exactly when Spring Data JPA started to support Hibernate 5. Please take a look at the documentation.
      JPQL supports only left joins and inner joins.

  3. Avatar photo Puspender says:

    Hello Thorben,
    In my case, the LocalDateTime and LocalDate persisted even without using the hibernate-java8 dependency. I am using the Spring-data-jpa and hibernate as it’s implementation. Is it handled by Spring?

  4. Avatar photo RichardWang says:

    this really helps. thanks a lot

  5. Avatar photo Rafael alvaro Ruiz tabares says:

    Thanks for this post

    1. Avatar photo Thorben Janssen says:

      Thanks, I updated the post.

  6. Hi Thorben,

    good post!

    I’m curious about what happens behind the scene. A LocalDateTime for instance has no time zone information. Will Hibernate use system default for the Timstamp? And if using the ZonedDateTime, will Hibernate use the associated Instant object?

    Kind regards,
    Henrik

  7. AttributeConverter cannot be used when the field is a primary key (nor part of a compound PK)

    if the LocalDate field is in fact a primary key, when running in wildlfy 10, will using this hibernate specific feature still work?

    1. Avatar photo Thorben Janssen says:

      Yes, it will work because Hibernate now supports LocalDate as native attribute. So, no AttributeConverter is needed.

Comments are closed.