java.time и JPA

Классы как LocalDateTime из пакета java.time являются основанными на значении классами. Если у меня есть объект, использующий такой объект как поле, я сталкиваюсь со следующей "проблемой": классы, основанные на значениях, не должны сериализоваться. Однако сущность JPA должна реализовать интерфейс Serializable. Каково решение этого парадокса? Не должен ли кто-то использовать LocalDateTime как поле сущности JPA? Использовать дату вместо? Это было бы неудовлетворительно.

Эта проблема является правилом сонара squid:S3437 и поэтому в проекте много ошибок, так как мы изменили с Date на LocalDateTime ...

Несоответствующее решение из-за использования класса на основе значения:

@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..
}

2 ответа

Решение

Мой ответ может показаться довольно прямым и бесполезным, но это больше для того, чтобы собрать вещи и подвести итог.

Во-первых, это то, что не существует "золотой пули" решения этой проблемы. Что-то определенно должно быть изменено, и я вижу 3 варианта или 3 альтернативы:

  1. Удалить Serializable интерфейс. Это не "хорошая практика" Serializable на всех сущностях. Это необходимо только в том случае, если вы собираетесь использовать его экземпляры как отдельные объекты: когда и почему сущности JPA должны реализовывать интерфейс Serializable?,

  2. Используйте тип Timestamp вместо LocalDateTime. Мне кажется, что это эквивалентно:

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

Мгновенные, LocalDateTime, OffsetDateTime и ZonedDateTime сопоставляются как значения отметок времени по умолчанию. Вы можете пометить свойство одного из этих типов с помощью @TeMPOraL, чтобы указать другую стратегию сохранения этого свойства.

  1. Если оба первых варианта у вас не работают, тогда (я уверен, вы знаете, что делать) - отключите это предупреждение @SuppressWarnings("squid:S3437"),

Я не совсем понимаю, что ваша БД принимает от jpa. Когда я имею дело с Postgres, я использую настроенный конвертер:

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

И я использую это так:

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

Видите ли, здесь я конвертирую LocalDateTime в Timestamp (принятый postgres) и обратно.

Другие вопросы по тегам