升级Oracle JDBC Driver从11到17触发ORA-18716时区错误求助
解决ojdbc17升级后ORA-18716时区敏感类型错误
问题场景
将Spring Boot 3.5.3应用的JDBC驱动版本从:
runtimeOnly "com.oracle.database.jdbc:ojdbc11:version: '21.8.0.0'" liquibaseRuntime "com.oracle.database.jdbc:ojdbc11:version: '21.8.0.0'"
升级为:
runtimeOnly "com.oracle.database.jdbc:ojdbc17:version: '23.8.0.25.04'" liquibaseRuntime "com.oracle.database.jdbc:ojdbc17:version: '23.8.0.25.04'"
后触发错误:
ORA-18716: {0} not in any time zone.DATE
错误提示说明使用OffsetDateTime、OffsetTime或ZonedDateTime等时区敏感类型时,输入数据必须包含时区信息。已实现DateMapper接口处理Instant到OffsetDateTime的转换:
public interface DateMapper { default OffsetDateTime fromInstant(Instant instant) { return Objects.isNull(instant) ? null : OffsetDateTime.ofInstant(instant, java.time.ZoneOffset.UTC); } }
但问题仍未解决,需技术帮助。
实用解决方向
1. 调整JDBC连接属性
ojdbc17对时区类型的校验比ojdbc11更严格,在数据库连接URL中添加以下参数:
jdbc:oracle:thin:@//your-db-host:port/service_name?oracle.jdbc.timezoneAsRegion=false&oracle.jdbc.timestampTzInGmt=true
oracle.jdbc.timezoneAsRegion=false:禁用时区区域名称解析,强制使用偏移量处理oracle.jdbc.timestampTzInGmt=true:将TIMESTAMP WITH TIME ZONE类型统一转换为GMT时区的OffsetDateTime,避免本地时区干扰
2. 对齐实体类与数据库列类型
确认实体类中使用OffsetDateTime等时区敏感类型的字段,对应的数据库列必须是TIMESTAMP WITH TIME ZONE,而非普通DATE或TIMESTAMP。如果无法修改数据库列类型,需调整实体类字段类型:
// 示例:将OffsetDateTime转为LocalDateTime(需确认业务允许丢失时区信息) default LocalDateTime offsetToLocal(OffsetDateTime offsetDateTime) { return offsetDateTime != null ? offsetDateTime.toLocalDateTime() : null; }
3. 修正Liquibase脚本的日期处理
升级liquibaseRuntime后,变更集里的日期操作也会触发严格校验:
- 插入/更新
TIMESTAMP WITH TIME ZONE列时,使用带时区的日期格式,比如'2024-01-01T12:00:00+00:00' - 生成日期时使用
SYSTIMESTAMP而非SYSDATE,确保返回带时区的类型
4. 配置Hibernate/JPA的时区映射(若使用)
如果项目依赖Hibernate,添加以下JPA配置:
spring.jpa.properties.hibernate.jdbc.time_zone=UTC spring.jpa.properties.hibernate.type.preferred_instant_jdbc_type=TIMESTAMP_WITH_TIMEZONE
让Hibernate自动将Instant/OffsetDateTime绑定到数据库的TIMESTAMP WITH TIME ZONE列,避免类型不匹配。
5. 覆盖所有日期转换场景
现有DateMapper仅处理了Instant到OffsetDateTime的转换,需排查其他未覆盖的场景:
- 数据库
DATE列直接映射到OffsetDateTime - 前端传入无时区标识的字符串日期转为OffsetDateTime
补充对应转换逻辑:
// 示例:将无时区字符串转为UTC时区的OffsetDateTime default OffsetDateTime stringToOffset(String dateStr) { if (dateStr == null) return null; LocalDateTime localDateTime = LocalDateTime.parse(dateStr); return localDateTime.atOffset(ZoneOffset.UTC); }
内容的提问来源于stack exchange,提问作者Murat K.
相关产品推荐
相关产品推荐

