Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

java.time and JPA

Classes as LocalDateTime from the package java.time are value based classes. If I have an entity using such an object as a field I run into the following "problem": Value based classes shouldn't be serialized. However, the JPA entity has to implement the interface Serializable. What is the solution for that paradox? Shouldn't someone use LocalDateTime as a field of an JPA entity? Use Date instead? This would be unsatisfying.

This problem is a Sonar rule squid:S3437 and therefore there are a lot of bugs in the project, since we changed from Date to LocalDateTime ...

Non compliant solution due to value based class usage:

@Entity
public class MyEntity implements Serializable{
    @Column
    private String id; // This is fine
    @Column
    private LocalDateTime updated; // This is not ok, as LocalDateTime is a value based class
    @Column
    private Date created; // This however is fine..
}
like image 793
itsme Avatar asked Dec 18 '17 09:12

itsme


2 Answers

My answer could seem quite direct and valueless, but it is more for getting things together and to summarise.

First thing, it that there is no "golden bullet" solution of this problem. Something definitely has to be changed and I see 3 options or 3 alternatives:

  1. Remove Serializable interface. It is not a "good practice" to put Serializable on all entities. It is needed only if you are going to use instances of it as detached objects: When and why JPA entities should implement Serializable interface?.

  2. Use Timestamp type instead of LocalDateTime. It seems to me that it is equivalent:

https://github.com/javaee/jpa-spec/issues/63

Instant, LocalDateTime, OffsetDateTime, and ZonedDateTime map as timestamp values by default. You may mark a property of one of these types with @TeMPOraL to specify a different strategy for persisting that property.

  1. If both first options do not work for you, then (I am pretty sure, you know what to do) - suppress this warning @SuppressWarnings("squid:S3437").
like image 138
Andremoniy Avatar answered Sep 28 '22 17:09

Andremoniy


I don't quite understand what you DB accepts from jpa. When I deal with Postgres, I use a customized converter:

import javax.persistence.AttributeConverter;
import javax.persistence.Converter;
import java.sql.Timestamp;
import java.time.LocalDateTime;

@Converter(autoApply = true)
public class LocalDateTimePersistenceConverter implements AttributeConverter<LocalDateTime, Timestamp> {

    @Override
    public Timestamp convertToDatabaseColumn(LocalDateTime locDateTime) {
        return (locDateTime == null ? null : Timestamp.valueOf(locDateTime));
    }

    @Override
    public LocalDateTime convertToEntityAttribute(Timestamp sqlTimestamp) {
        return (sqlTimestamp == null ? null : sqlTimestamp.toLocalDateTime());
    }
}

And I use it this way:

@Column(name = "create_date")
@Convert(converter = LocalDateTimePersistenceConverter.class)
private LocalDateTime createDate;

You see, here I convert LocalDateTime to Timestamp (accepted by postgres) and back.

like image 29
dimirsen Z Avatar answered Sep 28 '22 17:09

dimirsen Z