30

I want to store Date without time into my database. So, I choose to use LocalDate type.

As mentioned in this article: How to persist LocalDate and LocalDateTime with JPA 2.1, I use a JPA converter to convert LocalDate to Date.

However, I have some troubles when I want to persist my entity (with POST and PUT requests).

Error

2019-02-23 11:26:30.254  WARN 2720 --- [-auto-1-exec-10] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Expected array or string.; nested exception is com.fasterxml.jackson.databind.exc.MismatchedInputException: Expected array or string.
 at [Source: (PushbackInputStream); line: 1, column: 104] (through reference chain: ...entity.MyObject["startdate"])]

org.springframework.http.converter.HttpMessageConversionException: Type definition error: [simple type, class org.springframework.http.ResponseEntity]; nested exception is com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `org.springframework.http.ResponseEntity` (no Creators, like default construct, exist): cannot deserialize from Object value (no delegate- or property-based Creator)
 at [Source: (PushbackInputStream); line: 1, column: 2]

Code

Converter

package ...entity;

import javax.persistence.AttributeConverter;
import javax.persistence.Converter;
import java.time.LocalDate;
import java.sql.Date;

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

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

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

Entity

package ...entity;

import org.hibernate.annotations.ColumnDefault;

import javax.persistence.*;
import java.time.LocalDate;
import java.util.HashSet;
import java.util.Set;

@Entity
public class MyObject {

    @Id
    private String id;
    private LocalDate startdate;
    private LocalDate enddate;

    public MyObject() {}

    public MyObject(LocalDate enddate) {
        this.startdate = LocalDate.now();
        this.enddate = enddate;
    }

    ...
}

"Main"

private DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
MyObject myobject = new MyObject(LocalDate.parse("2019-03-01", formatter));

Thanks for help.

EDIT 1 : Print of MyObject

 HttpHeaders headers = new HttpHeaders();
 headers.setContentType(MediaType.APPLICATION_JSON);
 HttpEntity<String> entity = new HttpEntity<>(this.toJsonString(myObject), headers);
 System.out.println(entity.toString());

 // <{"id":"ba6649e4-6e65-4f54-8f1a-f8fc7143b05a","startdate":{"year":2019,"month":"FEBRUARY","dayOfMonth":23,"dayOfWeek":"SATURDAY","era":"CE","dayOfYear":54,"leapYear":false,"monthValue":2,"chronology":{"id":"ISO","calendarType":"iso8601"}},"enddate":{"year":2019,"month":"MARCH","dayOfMonth":1,"dayOfWeek":"FRIDAY","era":"CE","dayOfYear":60,"leapYear":false,"monthValue":3,"chronology":{"id":"ISO","calendarType":"iso8601"}}},[Content-Type:"application/json"]>
Manuel Jordan
  • 15,253
  • 21
  • 95
  • 158
Royce
  • 1,557
  • 5
  • 19
  • 44
  • so you have an error in JSON, which is not JPA. And even if it was JPA, you'd need to mention which JPA provider you use, and where you think this LocalDate is not being treated right. What does your debugging say –  Feb 23 '19 at 11:02
  • @BillyFrost I edited my question. I print `myObject` in `JSON`. The format is not the format specified in my formatter... – Royce Feb 23 '19 at 11:15
  • which does nothing to isolate WHERE the problem is. Is your problem in JPA not persisting data into your database, or not retrieving data from your database .... or is it just in converting a Java object into JSON ????? If the problem is in persistence then DEFINE what SQL is used to persist and where you think the problem is with that SQL ... and "persist" does not involve POST / PUT ... it involves JPA em.persist –  Feb 23 '19 at 11:39
  • So, what I don't understand is why the format into the `sysout()` is not `yyyy-MM-dd`. Because if I do `sysout(myObject.getStartdate())` the result is a date with `yyyy-MM-dd` format ... – Royce Feb 23 '19 at 11:48
  • 1
    Ok, so nothing to do with JPA then. A `LocalDate`.toString() method has its own output format defined by JAVADOCS, as per https://docs.oracle.com/javase/8/docs/api/java/time/LocalDate.html#toString-- Which has what to do with the "problem"? –  Feb 23 '19 at 13:09
  • @BillyFrost thanks. I will look that. – Royce Feb 23 '19 at 13:12

5 Answers5

31

With JPA 2.2, you no longer need to use converter it added support for the mapping of the following java.time types:

java.time.LocalDate
java.time.LocalTime
java.time.LocalDateTime
java.time.OffsetTime
java.time.OffsetDateTime
@Column(columnDefinition = "DATE")
private LocalDate date;
@Column(columnDefinition = "TIMESTAMP")
private LocalDateTime dateTime;
@Column(columnDefinition = "TIME")
private LocalTime localTime;
Michael Mesfin
  • 546
  • 4
  • 11
  • if i have LocalDate optional and i send it as null, oracle returns: expected date got binary, this does not happen if i am using java.util.Date. How can I solve it with jpa? – user666 Apr 16 '21 at 12:28
  • Optional class is a wrapper for potentially null values you shouldn't use it to wrap Entity fields but if you have to you can implement AttributeConverter, LocalDate> interface which maps optional fields to the actual value. – Michael Mesfin Feb 12 '23 at 17:13
  • @user666 did you find out how to get around that error? (expected Date...) ? – Kash. May 07 '23 at 00:07
19

JPA 2.2 supports LocalDate, so no converter is needed.

Hibernate also supports it as of 5.3 version.

Check out this article: What’s new in JPA 2.2 – Java 8 Date and Time Types for more details.

Manuel Jordan
  • 15,253
  • 21
  • 95
  • 158
Peter Šály
  • 2,848
  • 2
  • 12
  • 26
  • What is Hibernate using when mapping LocalDate? I need to adjust the datein case of LocalDate.MAX to, for instance 31.12.9999. I am using an AttributeConverter and it works, but java.sql.Date is horrible when mapping LocalDate.MIN because I need to allow 01.01.0001 but I cannot set java.sql.Date to anything lower than that. – lostiniceland Sep 24 '21 at 13:58
5

JPA 2.2 added support for mapping Java 8 Date/Time API, like LocalDate, LocalTime, LocalDateTime, OffsetDateTime or OffsetTime.

So, let's assume we have the following entity:

@Entity(name = "UserAccount")
@Table(name = "user_account")
public class UserAccount {

    @Id
    private Long id;

    @Column(name = "first_name", length = 50)
    private String firstName;

    @Column(name = "last_name", length = 50)
    private String lastName;

    @Column(name = "subscribed_on")
    private LocalDate subscribedOn;

    //Getters and setters omitted for brevity
}

Notice that the subscribedOn attribute is a LocalDate Java object.

When persisting the UserAccount:

UserAccount user = new UserAccount()
    .setId(1L)
    .setFirstName("Vlad")
    .setLastName("Mihalcea")
    .setSubscribedOn(
        LocalDate.of(
            2013, 9, 29
        )
    );

entityManager.persist(user);

Hibernate generates the proper SQL INSERT statement:

INSERT INTO user_account (
    first_name, 
    last_name, 
    subscribed_on, 
    id
) 
VALUES (
    'Vlad', 
    'Mihalcea', 
    '2013-09-29', 
    1
)

When fetching the UserAccount entity, we can see that the LocalDate is properly fetched from the database:

UserAccount userAccount = entityManager.find(
    UserAccount.class, 1L
);

assertEquals(
    LocalDate.of(
        2013, 9, 29
    ),
    userAccount.getSubscribedOn()
);
Vlad Mihalcea
  • 142,745
  • 71
  • 566
  • 911
4

Hibernate 5 supports java 8, so you can add this to your pom.xml:

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

This gives you mapping for LocalDate and LocalDateTime out of box.

Andronicus
  • 25,419
  • 17
  • 47
  • 88
2

I think you could write your own Converter, please check an answer: Spring Data JPA - Conversion failed when converting date and/or time from character string