How to persist LocalDate with JPA?

28,868

Solution 1

JPA 2.2 supports LocalDate, so no converter is needed.

Hibernate also supports it as of 5.3 version.

Check out this article for more details.

Solution 2

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;

Solution 3

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.

Solution 4

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

Solution 5

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

Share:
28,868
Royce
Author by

Royce

Updated on January 09, 2021

Comments

  • Royce
    Royce over 3 years

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

    As mentioned in this article, 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"]>
    
  • user666
    user666 about 3 years
    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?
  • lostiniceland
    lostiniceland over 2 years
    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<LocalDate, Date> 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.