58

I'm using Java 8 for my new project.

I'm trying to use new date and time api in java 8 however I don't know if JPA 2.1 fully supports this new Date and Time API or not.

Please share your experience/opinion in JPA`s supports for new date and time API in Java 8.

Can I use new date and time api in Java 8 safely with JPA 2.1?

UPDATE:

I'm using Hibernate (4.3.5.Final) as JPA implementation.

JodaStephen
  • 60,927
  • 15
  • 95
  • 117
TheKojuEffect
  • 20,103
  • 19
  • 89
  • 125
  • Have a look at this : http://javajeedevelopment.blogspot.fr/2016/11/datetime-jpahibernate-mapping.html – ben rhouma moez Nov 21 '16 at 13:13
  • This is the principal reason -not- to use Java reference types for temporal data in the database layer. If the database format for date-times is a numeric format, such as a Modified Julian Day number (can hold a date-time to millisecond precision in a double), there is never a problem with ORM support. Moreover, the Converter facility that is part of JPA 2.1 makes interconversion between numeric formats and objects in the time library of your choosing super easy. – scottb Oct 10 '17 at 16:59

8 Answers8

73

For Hibernate 5.X just add

    <dependency>
        <groupId>org.hibernate</groupId>
        <artifactId>hibernate-java8</artifactId>
        <version>${hibernate.version}</version>
    </dependency>

and

@NotNull
@Column(name = "date_time", nullable = false)
protected LocalDateTime dateTime;

will work without any additional effort. See https://hibernate.atlassian.net/browse/HHH-8844

UPDATE:

Please have a look at Jeff Morin comment: since Hibernate 5.2.x it is enough

 <dependency>
     <groupId>org.hibernate</groupId>
     <artifactId>hibernate-core</artifactId>
     <version>5.2.1.Final</version>
 </dependency>
 <dependency>
     <groupId>org.springframework</groupId>
     <artifactId>spring-...</artifactId>
     <version>4.3.1.RELEASE</version>
 </dependency>

See https://github.com/hibernate/hibernate-orm/wiki/Migration-Guide---5.2 and Integrate Hibernate 5.2 with Spring framework 4.x

Community
  • 1
  • 1
Grigory Kislin
  • 16,647
  • 10
  • 125
  • 197
  • 8
    Starting with Hibernate 5.2.x this addition is not necessary anymore since these Java 8 features were merged into Hibernate Core. – Jeff Morin Jul 15 '16 at 14:26
  • 1
    Thanks! Together with migration to Spring 4.3.x (http://stackoverflow.com/questions/37651837/integrate-hibernate-5-2-with-spring-framework-4-x) works! – Grigory Kislin Jul 15 '16 at 16:26
  • While I don't understand, what would happen if hibernate does not support new date api? Now for LocalDateTime columns they are stored as blob type in mysql, do you think this is the right behavior? – Tiina Jun 17 '17 at 06:21
21

JPA 2.1 is a spec that came out before Java 1.8, so doesn't mandate any support for it. Obviously some implementations may support some Java 1.8 features. Some have problems with Java 1.8 bytecode (e.g EclipseLink). I know DataNucleus supports java.time and Java 1.8 since that's the one I use. You'd have to check your implementation for what its support level is.

It has been requested that JPA 2.2 support the java.time types, see this issue https://java.net/jira/browse/JPA_SPEC-63

Neil Stockton
  • 11,383
  • 3
  • 34
  • 29
  • Thanks. I'm using Hibernate as JPA implementation as in updated question. – TheKojuEffect May 18 '14 at 06:10
  • 1
    Anyone can search for Hibernate support for such things. You find this https://hibernate.atlassian.net/browse/HHH-8844 so no, your selected JPA implementation doesn't support it – Neil Stockton May 18 '14 at 10:07
  • EclipseLink has no longer any problem with Java 8 bytecode. – eskatos Jun 06 '14 at 19:11
  • @eskatos, what is the version number of the release of EclipseLink that is fixed? I don't see a release since 26-Sep-2013 (v2.5.1) and none is present in Maven Central. – Neil Stockton Jun 07 '14 at 04:48
  • @NeilStockton I use the 2.5.1 release with Java 8 without any issue. That is `org.eclipse.persistence:eclipselink:2.5.1`. – eskatos Jun 07 '14 at 09:40
  • 2
    Oh, my bad, EclipseLink 2.5.1 fails if a lambda expression is present in an entity class. Not the case for me, that's why everything work has expected. See https://bugs.eclipse.org/bugs/show_bug.cgi?id=429992 – eskatos Jun 07 '14 at 09:42
  • 2
    The link is broken. – thatsIch Oct 17 '17 at 06:21
12

JPA 2.2 supports java.time

JPA 2.2 now supports LocalDate, LocalTime, LocalDateTime, OffsetTime and OffsetDateTime.

<dependency>
  <groupId>javax.persistence</groupId>
  <artifactId>javax.persistence-api</artifactId>
  <version>2.2</version>
</dependency>

For JPA 2.2 implementation, Hibernate 5.2 or EclipseLink 2.7 can be used.

Hibernate 5 supports more java types than JPA 2.2 like Duration, Instant and ZonedDateTime.

More Info:

TheKojuEffect
  • 20,103
  • 19
  • 89
  • 125
9

I am using Java 8, EclipseLink(JPA 2.1), PostgreSQL 9.3 and PostgreSQL Driver -Postgresql-9.2-1002.jdbc4.jar in my project and I can use LocalDateTime variables from the new API with no error, but the data type of the column will be bytea in the database. You can only read it from a Java application as far i know.

However, you can use AttributeConverter to convert these classes to java.sql.Date. I found this code from Java.net

@Converter(autoApply = true)
public class LocalDatePersistenceConverter implements
AttributeConverter {
@Override
public java.sql.Date convertToDatabaseColumn(LocalDate entityValue) {
    return java.sql.Date.valueOf(entityValue);
}

@Override
public LocalDate convertToEntityAttribute(java.sql.Date databaseValue) {
    return databaseValue.toLocalDate();
}
Kariem
  • 4,398
  • 3
  • 44
  • 73
Ismael_Diaz
  • 191
  • 7
  • 1
    You can use user-type for mapping. See this https://github.com/spring-projects/spring-data-examples/blob/master/jpa%2Fjava8%2Fsrc%2Fmain%2Fjava%2Fexample%2Fspringdata%2Fjpa%2Fjava8%2FAbstractEntity.java – TheKojuEffect Jul 04 '14 at 02:28
  • 3
    "with no problem but the data type of the column is bytea in the database" ... there is a problem from the start. A date/time should not be serialised! – Neil Stockton Apr 14 '16 at 17:18
  • it doesn´t if you use the converters to java.sql as i pointed. Sorry if i be unclear – Ismael_Diaz Apr 15 '16 at 21:07
9

org.jadira.usertype can be used to persist JSR 310 Date and Time API.

Check out this example project.

From Example Project,

@MappedSuperclass
public class AbstractEntity {

    @Id @GeneratedValue Long id;

    @CreatedDate//
    @Type(type = "org.jadira.usertype.dateandtime.threeten.PersistentZonedDateTime")//
    ZonedDateTime createdDate;

    @LastModifiedDate//
    @Type(type = "org.jadira.usertype.dateandtime.threeten.PersistentZonedDateTime")//
    ZonedDateTime modifiedDate;
}
TheKojuEffect
  • 20,103
  • 19
  • 89
  • 125
  • I am not sure but I think it should work with Eclipselink also. Try it out. – TheKojuEffect Dec 24 '14 at 04:44
  • 1
    The link to example project now uses some Spring specific annotation. Here is the link to older commit of this same project: https://github.com/spring-projects/spring-data-examples/blob/e953f6d5e5725bce65430d160e430f042c16a8c3/jpa/java8/src/main/java/example/springdata/jpa/java8/AbstractEntity.java (my edit request has been rejected) – Zhenya Apr 27 '15 at 09:57
3

I know that this is an old question but I thought of an alternative solution which might be helpful.

Instead of trying to map the new java.time.* classes to an existing database types, you can leverage @Transient:

@Entity
public class Person {
    private Long id;        
    private Timestamp createdTimestamp;

    @Id
    @GeneratedValue
    public Long getId() { return id; }

    private Timestamp getCreatedTimestamp() {
        return createdTime;
    }

    private void setCreatedTimestamp(final Timestamp ts) {
        this.createdTimestamp = ts;
    }

    @Transient
    public LocalDateTime getCreatedDateTime() {
        return createdTime.getLocalDateTime();
    }

    public void setCreatedDateTime(final LocalDateTime dt) {
        this.createdTime = Timestamp.valueOf(dt);
    }
}

You work with the public getter/setter methods that use the new Java 8 date/time classes, but behind the scenes the getter/setters work with the legacy date/time classes. When you persist the entity, the legacy date/time property will be persisted but not the new Java 8 property since it's annotated with @Transient.

Jin Kim
  • 16,562
  • 18
  • 60
  • 86
  • 2
    you forgot to declare the LocalDateTime createdTime variable in the code. – Ismael_Diaz Dec 23 '14 at 18:36
  • 3
    and also private getter/setter for persistent fields (the Timestamp) are not allowed in JPA specification it must be at least Protected but some JPA implementations ignore it and it works but not the standard – Ismael_Diaz Dec 23 '14 at 18:45
1

For type TIMESTAMP you can use this converter:

@Converter(autoApply = true)
public class LocalDateTimeAttributeConverter implements AttributeConverter<LocalDateTime, Timestamp> {

    @Override
    public Timestamp convertToDatabaseColumn(LocalDateTime datetime) {
        return datetime == null ? null : Timestamp.valueOf(datetime);
    }

    @Override
    public LocalDateTime convertToEntityAttribute(Timestamp timestamp) {
        return timestamp == null ? null : timestamp.toLocalDateTime();
    }

}

For type DATE you can use this converter:

@Converter(autoApply = true)
public class LocalDateAttributeConverter implements AttributeConverter<LocalDate, Date> {

    @Override
    public Date convertToDatabaseColumn(LocalDate date) {
        return date == null ? null : Date.valueOf(date);
    }

    @Override
    public LocalDate convertToEntityAttribute(Date date) {
        return date == null ? null : date.toLocalDate();
    }

}

For type TIME you can use this converter:

@Converter(autoApply = true)
public class LocalTimeAttributeConverter implements AttributeConverter<LocalTime, Time> {

    @Override
    public Time convertToDatabaseColumn(LocalTime time) {
        return time == null ? null : Time.valueOf(time);
    }

    @Override
    public LocalTime convertToEntityAttribute(Time time) {
        return time == null ? null : time.toLocalTime();
    }

}
Paco
  • 43
  • 3
0

There are Many approach to do , Also It depends to your frame work : If your frame work has on field Converter such spring do such: 1-

@DateTimeFormat(pattern = "dd.MM.yyyy - HH:mm")
private Long createdDate;

here I am using legacy epoch format https://www.epochconverter.com/ epoch is very flexible and accepted format

2- The other ways is to use jpa @PostLoad @PreUpdate @PrePersist

@PostLoad
      public void convert() {
        this.jva8Date= LocalDate.now().plusDays(1);
      }

or use temp one such

@Transient
public LocalDateTime getCreatedDateTime() {
    return createdTime.getLocalDateTime();
}
Ali.Mojtahed
  • 2,537
  • 1
  • 15
  • 23