Adam Bien's Weblog

New Java 8 Date and JPA 2.1 Integration

JPA 2.1 does not directly support the java.time API. However, with an AttributeConverter implementation you can easily integrate Java 8 (or any other dates) with JPA:


import java.time.Instant;
import java.time.LocalDate;
import java.util.Date;
import javax.persistence.AttributeConverter;
import javax.persistence.Converter;

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

    @Override
    public Date convertToDatabaseColumn(LocalDate date) {
        Instant instant = Instant.from(date);
        return Date.from(instant);
    }

    @Override
    public LocalDate convertToEntityAttribute(Date value) {
        Instant instant = value.toInstant();
        return LocalDate.from(instant);
    }
}


The @Converter(autoApply = true) will automatically activate the converter and keep your domain classes clean:

import java.time.LocalDate;

@Entity
public class Spaceship {

    @Id
    private long id;
    private String name;
    private int speed;
    private LocalDate arrival;
}

The example above was taken from Java 8 with Java EE 7. See you at http://airhacks.com or Virtual Dedicated Workshops / consulting!


NEW workshop: HTML 5 and JavaScript Essentials, Three days in April 2017: from Java EE 7 Architectures over Microservices to Performance, Troubleshooting and Monitoring
On demand workshops: Java EE 7: Bootstrap, Effective, Testing and Microservices available for streaming.

Newsletter: airhacks.news

A book about rethinking Java EE Patterns

Comments:

Hi Adam,

I am using almost the same instantconverters. However, I check if the attribute is null first. You get a NullPointerException if your database LocalDate is null. Further I use java.sql.Date as the returntype instead of java.util.Date. Here's my code:

@Override
public Date convertToDatabaseColumn(LocalDate attribute) {
if (attribute != null) {
return Date.valueOf(attribute);
} else return null;
}

@Override
public LocalDate convertToEntityAttribute(Date dbData) {
if (dbData != null) {
return dbData.toLocalDate();
} else return null;
}

Posted by Martijn Burger on December 30, 2014 at 01:15 PM CET #

The conversion "Instant.from(date)" will fail because of missing timezone information.

The same remark is valid for the reverse transformation "LocalDate.from(instant)", too.

Just try and you will see an exception in any of these cases.

Posted by Meno Hochschild on January 02, 2015 at 07:17 AM CET #

See this AttributeConverter [0] about to be shipped with Spring Data JPA to convert all non-time-zoned JSR-310 types into Dates automatically.

The actual implementation of the conversion can be found here [1].

Cheers,
Ollie

[0] https://github.com/spring-projects/spring-data-jpa/blob/703f5f812c0b517b900296a97440911fe3c5173d/src/main/java/org/springframework/data/jpa/domain/support/Jsr310JpaConverters.java#L45
[1] https://github.com/spring-projects/spring-data-commons/blob/23cad97b9a59171bb680b893687ba7bf689147dc/src/main/java/org/springframework/data/convert/Jsr310Converters.java#L40

Posted by Ollie on January 04, 2015 at 04:52 PM CET #

Should be something like this for the time being, I suppose:

@Converter(autoApply = true)
public class LocalDateConverter implements AttributeConverter<LocalDate, Date> {
@Override
public Date convertToDatabaseColumn(LocalDate date) {
if (date == null) {
return null;
}
final Instant instant = date.atStartOfDay().atZone(ZoneId.systemDefault()).toInstant();
return Date.from(instant);
}

@Override
public LocalDate convertToEntityAttribute(Date value) {
if (value == null) {
return null;
}
final Instant instant = Instant.ofEpochMilli(value.getTime());
return LocalDateTime.ofInstant(instant, ZoneId.systemDefault()).toLocalDate();
}
}

with java.util.Date

Nice article by the way, thanks for sharing the knowledge.

Posted by Gabor Hajba on January 05, 2015 at 04:08 PM CET #

Hi Adam,

Could you please explain the best way to convert to/from java.time.LocalDate in JAX-RS when the date is a single request input and when the date is a field in another class?

What I've found is either to use a MessageBodyReader (have to write a reader/writer for each class holding a LocalDate) or extend the javax.xml.bind.annotation.adapters.XmlAdapter and apply the adapter to each LocalDate field. See my question at http://stackoverflow.com/questions/27768866/jax-rs-and-java-util-localdate-as-inputparameter/27772267#27772267.

//JR

Posted by Jørgen Ringen on January 05, 2015 at 08:18 PM CET #

for AttributeConverter<JSR310,java.sql.*>, you can also use:
java.sql.Date.valueOf(LocalDate)
java.sql.Time.valueOf(LocalTime)
java.sql.Timestamp.valueOf(LocalDateTime)

Posted by 158.169.40.5 on January 16, 2015 at 03:23 PM CET #

The conversion shown in the example are not working. It resulted in the error message "java.time.DateTimeException: Unable to obtain Instant from TemporalAccessor". See http://stackoverflow.com/questions/19431234/ for more details.

Here is a working solution (in Kotlin):
import java.time.LocalDateTime
import java.time.ZoneId
import java.util.*
import javax.persistence.AttributeConverter
import javax.persistence.Converter

@Converter(autoApply = true)
public class LocalDateConverter : AttributeConverter<LocalDateTime, Date> {

override fun convertToDatabaseColumn(dateTime: LocalDateTime): Date {
return Date.from(dateTime.atZone(ZoneId.systemDefault()).toInstant())
}

override fun convertToEntityAttribute(value: Date): LocalDateTime {
val instant = value.toInstant()
return LocalDateTime.ofInstant(instant, ZoneId.systemDefault())
}
}

PS: Some markup support would be great for a programming blog.

Posted by Christian on December 03, 2015 at 09:32 PM CET #

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

public Date convertToDatabaseColumn(LocalDate localDate) {
return localDate != null ? Date.valueOf(localDate) : null;
}

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

Posted by Jakob Bräuchi on February 11, 2016 at 09:05 PM CET #

Given that the type of the underlying database column is SQL DATE, would it be possible to use an AttributeConverter<LocalDate, String> instead?
That way, the implicit dependency on system timezone, which comes with java.sql.Date, could potentially be avoided.
See my stackoverflow question (answered by Tanuki in a comment):
http://stackoverflow.com/questions/37779418/how-to-map-sql-date-to-localdate
Best regards, Lars

Posted by Lars on June 13, 2016 at 06:35 AM CEST #

I can't find the way how to use the Converter from included (my) JAR file.
We have a common set of utilities/helpers for all (micro)services WAR files in a JAR, but loading a LocalDateConverter from such a JAR does not work.
How to achieve this? Thanx!

Posted by Michal on April 14, 2017 at 01:36 PM CEST #

Post a Comment:
  • HTML Syntax: NOT allowed
realworldpatterns.com
Online Workshops
...the last 150 posts
...the last 10 comments
License