TheKojuEffect
TheKojuEffect

Reputation: 21081

JPA support for Java 8 new date and time API

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.

Upvotes: 58

Views: 52025

Answers (8)

Ismael_Diaz
Ismael_Diaz

Reputation: 191

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

Upvotes: 9

TheKojuEffect
TheKojuEffect

Reputation: 21081

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:

Upvotes: 13

Paco
Paco

Reputation: 53

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

}

Upvotes: 1

Ali.Mojtahed
Ali.Mojtahed

Reputation: 2587

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

Upvotes: 0

Grigory Kislin
Grigory Kislin

Reputation: 17980

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

Upvotes: 73

Neil Stockton
Neil Stockton

Reputation: 11531

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

Upvotes: 21

Jin Kim
Jin Kim

Reputation: 17722

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.

Upvotes: 3

TheKojuEffect
TheKojuEffect

Reputation: 21081

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

Upvotes: 9

Related Questions