我正在将Sping Boot 2 + Hibernate 5应用程序升级到Spring Boot 3和Hibernate 6。在更新时,我注意到Hibernate 6处理java即时字段到不带时区信息的表列之间的转换的方式与Hibernate 5不同。
在Hibernate 6中,当持久化一个示例字段值为2023-02-20T04:08:00Z
的对象时,该值在检索时被Map到2023-02-20T05:08:00Z
。正如here所解释的,这可能与MapInstant和Duration字段的新方法有关。
我设法通过向应用程序中每个@Entity
类的每个Instant
字段添加@JdbcType(InstantAsTimestampJdbcType.class)
来解决这个问题。但是,我更愿意为这个问题进行单个集中配置,而不是向应用程序中的每个Instant字段添加注解。我尝试了几种方法:
选项1:添加属性hibernate.timezone.default_storage
并尝试所有存储类型选项(源代码):
spring.jpa.properties.hibernate.timezone.default_storage=NORMALIZE_UTC
选项2:声明Hibernate属性定制器Bean:
@Bean
public HibernatePropertiesCustomizer hibernatePropertiesCustomizerPG() {
return hibernateProperties -> {
hibernateProperties.put(EntityManagerFactoryBuilderImpl.TYPE_CONTRIBUTORS,
(TypeContributorList) () -> List.of(
(TypeContributions typeContributions, ServiceRegistry serviceRegistry) ->
typeContributions.contributeJdbcType(InstantAsTimestampJdbcType.INSTANCE)));
};
}
选项3:创建HibernatePropertiesCustomizer
:
@Configuration
public class HibernateTypeContributorConfiguration implements HibernatePropertiesCustomizer {
@Override
public void customize(Map<String, Object> hibernateProperties) {
hibernateProperties.put(
EntityManagerFactoryBuilderImpl.TYPE_CONTRIBUTORS,
(TypeContributorList) () -> List.of((jdbcType, serviceRegistry) -> jdbcType.contributeJdbcType(InstantAsTimestampJdbcType.INSTANCE))
);
}
}
不幸的是,到目前为止没有一种方法起作用。有人知道如何注册InstantAsTimestampJdbcType
吗?或者有更好的方法来解决这个问题吗?
- 请注意,不能通过修改表列来添加时区信息。*
1条答案
按热度按时间gz5pxeao1#
默认情况下,Hibernate 6将
Instant
Map到SQL类型代码SqlTypes.TIMESTAMP_UTC
。此类型代码(默认情况下)Map到InstantAsTimestampWithTimeZoneJdbcType
或InstantAsTimestampJdbcType
,具体取决于Dialect
的TimeZoneSupport
的功能。这两个JdbcType
都将在将Instant
发送到数据库之前将其转换为以UTC表示的时间戳。如果您设置了
hibernate.type.preferred_instant_jdbc_type=TIMESTAMP
,那么这将强制Hibernate使用TimestampJdbcType
,它将 * 不 * 标准化为UTC,而只是将Instant
转换为本地时间戳并将其发送到数据库。你能试试吗,然后告诉我是否有你想要的效果?
PS请注意,使用
@JdbcTypeCode(TIMESTAMP)
或@JdbcType(TimestampJdbcType.class)
应与配置属性具有相同的效果。