JPA support for Java 8 new date and time API

JavaJpaJava 8Jpa 2.1Java Time

Java Problem Overview


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.

Java Solutions


Solution 1 - Java

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 https://stackoverflow.com/questions/37651837/integrate-hibernate-5-2-with-spring-framework-4-x

Solution 2 - Java

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

Solution 3 - Java

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:

Solution 4 - Java

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();
}

Solution 5 - Java

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;
}

Solution 6 - Java

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.

Solution 7 - Java

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();
	}

}

Solution 8 - Java

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();
}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionTheKojuEffectView Question on Stackoverflow
Solution 1 - JavaGrigory KislinView Answer on Stackoverflow
Solution 2 - JavaNeil StocktonView Answer on Stackoverflow
Solution 3 - JavaTheKojuEffectView Answer on Stackoverflow
Solution 4 - JavaIsmael_DiazView Answer on Stackoverflow
Solution 5 - JavaTheKojuEffectView Answer on Stackoverflow
Solution 6 - JavaJin KimView Answer on Stackoverflow
Solution 7 - JavaPacoView Answer on Stackoverflow
Solution 8 - JavaAli.MojtehedyView Answer on Stackoverflow