Некорректное преобразование timestamp в Instant (Hibernate)

Рейтинг: 1Ответов: 0Опубликовано: 28.02.2023

Исходная информация

SpringBoot (2.7.7)-приложение, MS SQL 2014
в настройках DataSource подключения к БД диалект выставлен в org.hibernate.dialect.SQLServerDialect,
драйвер com.microsoft.sqlserver.jdbc.SQLServerDriver,
в yaml-файле выставлено hibernate.jdbc.time_zone = UTC

В БД есть таблица с полями типа datetime:

CREATE TABLE [dbo].[some_table](
    [id] [int] IDENTITY(1,1) NOT NULL,
    [some_datetime_field] [datetime] NULL
)

В этой таблице в одной из записей содержится нулевое значение даты (0 по ISO = 1899-12-30 00:00:00.000) - достаточно штатная ситуация для нашего ПО

Это поле мапится в тип Instant:

@Entity
@Table(name = "some_table")
public class SomeEntity{
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id", nullable = false)
    private Integer id;

    @Column(name = "some_datetime_field")
    private Instant datetimeField;
}

Проблема

Вместо даты 1899-12-30 00:00:00.000 в это поле приходит значение, зависящее от сервера. В частности, на одном из них получается 1899-12-30T00:29:43, т.е. на 29 минут 43 секунды больше номинала. При этом даты, больше приближенные к реальности (2022-2024 года), отображаются без нарушений.

Генерируемые hibernate-ом запросы проверил, никаких преобразований в них нет - обычные plain select-ы. Собственных конвертеров тоже нет, используются только штатные.

Если задействовать кастомный конвертер (AttributeConverter<Instant, Timestamp>) - то данные получаются корректно, но писать это над каждым Instant-полем (их очень много) совсем не вариант.


Закопавшись дебагом в глубины Hibernate-а выяснил, что виной всему метод org.hibernate.type.descriptor.java.InstantJavaDescriptor.wrap. В нём есть следующая строка:

return ts.getYear() < 5 ? ts.toLocalDateTime().atZone(ZoneId.systemDefault()).toInstant() : ts.toInstant();

здесь ts - это полученный из ResultSet-а Timestamp (причем на этом этапе данные в нём корректны).
А дальше начинается фокус: getYear для 1899 года возвращает (-1). И тогда идёт преобразование в Instant через системную локаль (зачем???). Которая, видимо, для таких далёких дат просто не содержит валидных значений.

Вопрос

Можно ли заменить штатный для Hibernate InstantJavaDescriptor на свой собственный, в котором я смогу убрать это странное преобразование в Instant через локаль сервера?
Цель - сделать это однократно, как (например) для jackson есть возможность зарегистрировать свои конвертеры (и чем пользуется jsr310). Повторюсь - аннотировать каждое Instant-поле собственным конвертером совсем не вариант.

Ответы

Ответов пока нет.