简体   繁体   中英

JPA Cannot Deserialize Java 8 LocalDateTime

I'm using Spring Boot 1.5.1 and getting an exception anytime I hit my API when there is a LocalDateTime field in my Entity class.

The MySQL dt column is TIMESTAMP

Is JPA not able to natively deserialize LocalDateTime?

Console output when performing GET request

 2017-03-02 22:00:18.797 ERROR 13736 --- [nio-8080-exec-1] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.orm.jpa.JpaSystemException: could not deserialize; nested exception is org.hibernate.type.SerializationException: could not deserialize] with root cause

    java.io.StreamCorruptedException: invalid stream header: 20323031

Reservation.class

package com.example.springboot.reservation;

import java.time.LocalDateTime;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;

// Model class 

@Entity
@Table(name="reservation")
public class Reservation {

    @Id
    private Long id;

    @Column
    private LocalDateTime dt;


    @Column(name="user_id")
    private Long userId;

    // Hibernate will convert camel case column names to snake case!!!
    // Don't use camelcase columns in DB
    @Column(name="party_size")
    private int partySize;

    public Reservation() {}

    public Reservation(Long id,  Long userId, int partySize) {
        this.id = id;

        this.userId = userId;
        this.partySize = partySize;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public LocalDateTime getDt() {
        return dt;
    }

    public void setDt(LocalDateTime dt) {
        this.dt = dt;
    }

    public Long getUserId() {
        return userId;
    }

    public void setUserId(Long userId) {
        this.userId = userId;
    }

    public int getPartySize() {
        return partySize;
    }

    public void setPartySize(int partySize) {
        this.partySize = partySize;
    }

}

pom.xml

<parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.1.RELEASE</version>
  </parent>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

  <properties>
    <java.version>1.8</java.version>
  </properties>
@Converter
public class LocalDateTimeConverter implements AttributeConverter<java.time.LocalDateTime, java.sql.Timestamp> {

  @Override
  public java.sql.Timestamp convertToDatabaseColumn(java.time.LocalDateTime entityValue) {
    return entityValue == null ? null : java.sql.Timestamp.valueOf(entityValue)
  }

  @Override
  public java.time.LocalDateTime convertToEntityAttribute(java.sql.Timestamp dbValue) {
    return dbValue == null ? null : dbValue.toLocalDateTime(); 
  }
}

Make sure that this converter class is added to the package scanned by hibernate. Add this converter to the column declaration

@Column
@Convert(converter = LocalDateTimeConverter.class)
private LocalDateTime dt;

If you are not using JPA 2.0, This answer would help you use @Temporal annotation for LocalDateTime.

You don't need a converter if you use the newer hibernate-java8 which has LocalDateTime support.

<dependency>
  <groupId>org.hibernate</groupId>
  <artifactId>hibernate-java8</artifactId>
  <version>${hibernate.version}</version>
</dependency>

You can write converter as described below:

@Converter(autoApply = true)
public class MyLocalDateConverter implements AttributeConverter<java.time.LocalDate, java.sql.Date> {

   @Override
   public java.sql.Date convertToDatabaseColumn(java.time.LocalDate attribute) {
      return attribute == null ? null : java.sql.Date.valueOf(attribute);
   }

   @Override
   public java.time.LocalDate convertToEntityAttribute(java.sql.Date dbData) {
    return dbData == null ? null : dbData.toLocalDate();
   }
}

You can find some already baked converters in spring package:

org.springframework.data.jpa.convert.threeten.Jsr310JpaConverters

JDocs:

JPA 2.1 converters to turn JSR-310 types into legacy Dates. To activate these converters make sure your persistence provider detects them by including this class in the list of mapped classes. In Spring environments, you can simply register the package of this class (ie org.springframework.data.jpa.convert.threeten) as package to be scanned on eg the LocalContainerEntityManagerFactoryBean.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM