Некорректное преобразование timestamp в Instant (Hibernate)
Исходная информация
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-поле собственным конвертером совсем не вариант.