7

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..
}
Andremoniy
  • 34,031
  • 20
  • 135
  • 241
itsme
  • 852
  • 1
  • 10
  • 23
  • 5
    Why in hell do you think value based classes shouldn't be serialized? Of course they can be serialized. That's why it implements Serializable. – JB Nizet Dec 18 '17 at 09:29
  • well we have this story as-well, for the time being we are just `@SuppressWarnings("squid:S3437")` for now, there is a comment on dev-list (I'll try to dig it up) where it says that this *might* prohibit these value based *classes* to be moved to value based *types* – Eugene Dec 18 '17 at 09:31
  • @JBNizet That's what is really odd about LocalDateTime. It is also a value based class.If you look into the oracle documentation you'll find the following statement about value based classes: https://docs.oracle.com/javase/8/docs/api/java/lang/doc-files/ValueBased.html A program may produce unpredictable results if it attempts to distinguish two references to equal values of a value-based class [...] serialization, or any other identity-sensitive mechanism. Use of such [...] may have unpredictable effects and should be avoided. – itsme Dec 18 '17 at 09:31
  • Take a look on [this](http://www.adam-bien.com/roller/abien/entry/new_java_8_date_and) might help to get rid of 'bugs'. – pirho Dec 18 '17 at 09:31
  • @pirho This is not a solution to my problem – itsme Dec 18 '17 at 09:33
  • Yes. the sentence says: "A program may produce unpredictable results if it attempts to distinguish two references to equal values of a value-based class [...] via [...] serialization. So, in short, it says that you shouldn't use serialization to create a copy of a value-based class and rely on the fact that the copy will indeed be a copy. That doesn't mean you cannot serialize it. – JB Nizet Dec 18 '17 at 09:33
  • In fact this is a good question, as squid:S3437 is based on something like this: https://stackoverflow.com/questions/26451590/why-should-javas-value-based-classes-not-be-serialized – Andremoniy Dec 18 '17 at 09:44
  • @Andremoniy also this http://mail.openjdk.java.net/pipermail/valhalla-dev/2015-February/001043.html, where Brian says this: *So serializability is one of those things that might inhibit migrating a value-based class to a true value type* – Eugene Dec 18 '17 at 09:49
  • @Andremoniy we would be really happy to hear how and what we could improve for your SonarQube experience, feel free to share your concerns sonarqube@googlegroups.com – benzonico Dec 18 '17 at 09:56
  • 1
    @itsme Why do you think that "JPA entity has to implement the interface Serializable"? This is only required if you want to serialize it e.g. using EJB remoting. – Simon Martinelli Dec 18 '17 at 10:14
  • @SimonMartinelli you're right, it doesn't have to be Serializable, at least together with hibernate. However for me it's kind of a best practice. Also it's not very relevant for this question – itsme Dec 18 '17 at 10:27

2 Answers2

2

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").
Andremoniy
  • 34,031
  • 20
  • 135
  • 241
  • 1
    I mark your answer as the solution, as it can be used to workaround the "problem". However I don't feel the relief, that I feel normally when I have a solution for a problem :) The reason for that is: Why on earth is LocalDateTime both value based and Serializable. This seems very odd... – itsme Dec 19 '17 at 13:05
  • 1
    @itsme the same applies for `immutable` collections in JDK-9, they are declared to be value-types in the documentation but they implement `Serializable` – Eugene Dec 21 '17 at 13:00
  • @itsme what do you mean why? how would you pass a `List` of something along the wire if it weren't `Serializable`? – Eugene Dec 22 '17 at 12:47
  • @Eugene Sorry, I wasn't specific enough. I mean why is something like `LocalDateTime` or as you said the `immutable` collections of JDK-9 value based but serializable at the same time. At the same time JDK documentation states, that serializing value based classes can result in unpredictable results (see https://docs.oracle.com/javase/8/docs/api/java/lang/doc-files/ValueBased.html) – itsme Dec 29 '17 at 02:14
  • No, `java.sql.Timestamp` is *not* at all equivalent to `java.time.LocalDateTime`! The first represents a moment, a specific point on the timeline. The second does not, lacking any concept of time zone or offset-from-UTC. Read their Javadoc. – Basil Bourque Jul 08 '19 at 15:49
1

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.

dimirsen Z
  • 873
  • 13
  • 16
  • My question was not how to use LocalDateTime with JPA, but how to solve the contradiction of using a class that is serializable but also value based. Please read my question carefully ;) This doesn't answer it – itsme Dec 19 '17 at 13:01
  • The way you composed your question is difficult to understand. Serializable is a marker and you don't have to implement any method. It just tells that an instance of this class can be transferred via RMI or saved in the file system. No obligations. There is no restriction that you shouldn't use LocalDateTime as a field of JPA entity and I've posted a working example of that. LocalDateTime can be converted and you have a sample code now. – dimirsen Z Dec 19 '17 at 13:36
  • Thanks for you answer, but nowhere in my question I mentioned that the conversion of the LocalDateTime is not working. It is a general question about serializable <-> value based and why LocalDateTime has both of it, but the same time it should not be done like that – itsme Dec 20 '17 at 14:37