Hibernate持久化LocalTime与OffsetTime时时间值异常
问题:Hibernate持久化LocalTime/OffsetTime到PostgreSQL时出现时区转换错误
我推送了一个可测试的最小示例,尝试持久化包含LocalTime和OffsetTime属性的实体,但15:30的时间被持久化为LocalTime类型的14:30,OffsetTime类型则被持久化为14:30减去偏移量。相关讨论尚未得出合适解决方案——直接查看数据库会得到错误的14:30时间值,求修复方法。
环境关键配置
PostgreSQL Docker容器(Compose文件)
postgres: image: 'postgres:15.2-alpine' container_name: postgres environment: POSTGRES_DB: db POSTGRES_USER: user POSTGRES_PASSWORD: pass TZ: UTC ports: - "5432:5432" command: ["postgres", "-c", "log_statement=all"]
实体类
@Entity public class TimeEntity { @Id @GeneratedValue private Long id; private LocalTime localTime = LocalTime.of(15,30,30); private OffsetTime offsetTime = OffsetTime.of(15,30,30,0,ZoneOffset.of("+02:00")); private OffsetDateTime offsetDateTime = OffsetDateTime.of(2023,1,1,15,30,30,0, ZoneOffset.of("+02:00")); }
Spring配置(application.yml)
spring.jpa.properties.hibernate.jdbc.time_zone: UTC
日志信息
Hibernate INSERT日志
insert into time_entity (local_time,offset_date_time,offset_time,id) values (?,?,?,?) 2023-10-13T19:45:47.659+02:00 TRACE 246595 --- [ main] org.hibernate.orm.jdbc.bind : binding parameter [1] as [TIME] - [15:30:30] 2023-10-13T19:45:47.659+02:00 TRACE 246595 --- [ main] org.hibernate.orm.jdbc.bind : binding parameter [2] as [TIMESTAMP_UTC] - [2023-01-01T15:30:30+02:00] 2023-10-13T19:45:47.659+02:00 TRACE 246595 --- [ main] org.hibernate.orm.jdbc.bind : binding parameter [3] as [TIME_UTC] - [15:30:30+02:00] 2023-10-13T19:45:47.659+02:00 TRACE 246595 --- [ main] org.hibernate.orm.jdbc.bind : binding parameter [4] as [BIGINT] - [1]
PostgreSQL容器INSERT日志
insert into time_entity (local_time,offset_date_time,offset_time,id) values ($1,$2,$3,$4) 2023-10-13T17:45:47.661272468Z 2023-10-13 17:45:47.661 UTC [511] DETAIL: parameters: $1 = '14:30:30', $2 = '2023-01-01 14:30:30+01', $3 = '12:30:30', $4 = '1'
修复方案
1. 问题根源
hibernate.jdbc.time_zone=UTC会强制Hibernate将所有时间类型转换为UTC后写入数据库,但LocalTime本身是无时区的时间,这种转换完全不合理;而OffsetTime的时区偏移会在转换为UTC后丢失,因为PostgreSQL的TIME类型不存储时区信息。
2. 修复LocalTime存储错误
给LocalTime字段添加注解,强制Hibernate不做时区转换,直接原样存储:
@Column(columnDefinition = "TIME WITHOUT TIME ZONE") private LocalTime localTime = LocalTime.of(15,30,30);
3. 修复OffsetTime存储错误
如果需要保留时区偏移,不要用PostgreSQL的TIME类型,改用带时区的时间戳类型:
@Column(columnDefinition = "TIMESTAMP WITH TIME ZONE") private OffsetTime offsetTime = OffsetTime.of(15,30,30,0,ZoneOffset.of("+02:00"));
如果不需要保留时区,只想让存储值和原始值一致,同样禁用时区转换:
@Column(columnDefinition = "TIME WITHOUT TIME ZONE") private OffsetTime offsetTime = OffsetTime.of(15,30,30,0,ZoneOffset.of("+02:00"));
4. 全局配置优化(可选)
如果不想逐个字段配置,可通过全局设置禁用LocalTime的时区转换(适用于Hibernate 6+):
spring.jpa.properties.hibernate.time.default_timezone: UTC spring.jpa.properties.hibernate.time.convert_local_time: false
升级到Hibernate 6.x版本也能改善时间类型的时区处理逻辑,减少此类问题。
5. 验证
修改后重新运行测试,查看PostgreSQL日志确认参数值与原始时间一致,直接查询数据库即可得到正确的15:30时间值。
内容的提问来源于stack exchange,提问作者bentinhoj
相关产品推荐
相关产品推荐

