信创-为什么ORACLE使用JDBC查询SYSDATE时,RS.getDate能获取到时间部分?

信创-为什么ORACLE使用JDBC查询SYSDATE时,RS.getDate能获取到时间部分?

1. 这是个什问题?

在某些版本ORACLE对应的JDBC驱动中,使用查询语句select sysdate from dual,使用rs.getDate获取到日期包含时间部分.而其他大部分数据库返回都不会包含时间部分.间接造成在做信创迁移时,造成时间部分丢失.

由于测试代码简单,这里不提供代码部分,直接测试Oracle mysql两种数据的情况,同时经过测试返现,对应的kingbase guassdb同样有一下情况

txt 复制代码
✅ [oracle] 数据库连接已建立
当前查询SQL:select  sysdate from dual
Date类型值=>93
getDate_FMT =>  2026-05-03 21:30:34
getDate=>          2026-05-03
getTimestamp=>2026-05-03 21:30:34.0
✅ [oracle] 数据库连接已关闭
✅ [mysql] 数据库连接已建立
当前查询SQL:select  sysdate()
Date类型值=>93
getDate_FMT =>  2026-05-03 00:00:00
getDate=>          2026-05-03
getTimestamp=>2026-05-03 21:30:34.0
✅ [mysql] 数据库连接已关闭

针对以上输出结果可以反应了以下信息:

  • ORACLE 和 MYSQL日期列对应的TYPE值都是 93
  • 使用格式yyyy-MM-dd HH:mm:ss 格式化,可以发现ORACL包含时间部分21:30:34
  • 当前的查询sql,复制到数据执行,不管是oracle还是mysql查询接口都包含了时间部分

以下是直接在mysql数据库执行的情况

scss 复制代码
sysdate()          |
-------------------|
2026-05-03 21:36:24|

通过这些输出信息可以得到一个结论:数据库底层查询sysdate都是包含时间部分的,只是有些JDBC的驱动去掉了时间部分

2. Oracle的JDBC驱动是如何处理的呢?

带着上边的疑问,我分析了ojdbc8-19.7.0.0.jar的源码,找到对应的处理逻辑.由此发现,Oracle针对这种情况,还提供了专门的配置oracle.jdbc.DateZeroTime,让用户确定是否保留日期类型的时间部分

那这部分代码到底在哪里呢?以下是我定位到堆栈信息

txt 复制代码
T4CDateAccessor(DateTimeCommonAccessor).getDate(int) line: 135	
T4CStatement(GeneratedStatement).getDate(long, int) line: 163	
ForwardOnlyResultSet(GeneratedScrollableResultSet).getDate(int) line: 186	
Multdbtest.testSysdate() line: 138	

由此我们可以知道,Oralce处理日期的类是 DateTimeCommonAccessor

我们看下这部分代码

java 复制代码
Date getDate(int paramInt, Calendar paramCalendar) throws SQLException {
    Calendar calendar;
    if (isNull(paramInt)) return null;
    if (paramCalendar == null) {
        calendar = this.statement.getDefaultCalendar();
    } else {
        calendar = (Calendar)paramCalendar.clone();
    }
    getBytesInternal(paramInt, this.tmpBytes);
    int i = oracleYear(this.tmpBytes);
    calendar.clear();
    calendar.set(1, i);
    calendar.set(2, oracleMonth(this.tmpBytes));
    calendar.set(5, oracleDay(this.tmpBytes));
    if (OracleDriver.getSystemPropertyDateZeroTime()) {
        calendar.set(11, 0);
        calendar.set(12, 0);
        calendar.set(13, 0);
    } else {
        calendar.set(11, oracleHour(this.tmpBytes));
        calendar.set(12, oracleMin(this.tmpBytes));
        calendar.set(13, oracleSec(this.tmpBytes));
    }
    calendar.set(14, 0);
    if (i > 0 && calendar.isSet(0)) {
        calendar.set(0, 1);
    }
    return new Date(calendar.getTimeInMillis());
}

其中有一部分关键代码 if (OracleDriver.getSystemPropertyDateZeroTime()) 如果这个值为true,就直接把时间部分值设置成0了,否则,就取数据返回的时间部分的信息.

OracleDriver.getSystemPropertyDateZeroTime 这个函数,从函数名基本可以知道它的意图,应该是获取 SystemProperty ,这些信息可以通过启动时注入.那我们推测是否是正确的呢?

java 复制代码
public static boolean getSystemPropertyDateZeroTime() {
    String str = PhysicalConnection.getSystemPropertyDateZeroTime("false");
    return str.equalsIgnoreCase("true");
}

从这里代码只知道,可以大概推测到,这方法默认值返回的是false,就是要获取数据库返回的时间部分,我们继续往下追代码

java 复制代码
static String getSystemPropertyDateZeroTime(String defaultValue) {
    return getSystemProperty("oracle.jdbc.DateZeroTime", defaultValue);
}

private static String getSystemProperty(String configKey, String defaultValue) {
    if (configKey != null) {
        final String fstr = configKey;
        final String fdefaultValue = defaultValue;
        final String[] rets = { defaultValue };
        AccessController.doPrivileged(new PrivilegedAction() {
            public Object run() {
                rets[0] = System.getProperty(fstr, fdefaultValue);
                return null;
            }
        });
        return rets[0];
    } 
    return defaultValue;
}

3. 知道原因,那如何使用呢?

目前知道时间部分存在的原因,如果目前希望rs.getDate返回的日期,跟其他数据库保一样不返回时间部分,就可以设置oracle.jdbc.DateZeroTime=true,对应测试代码如下:

java 复制代码
String exesql = "select  sysdate ";
if (dbName == "oracle") {
	exesql += " from dual";
	System.setProperty("oracle.jdbc.DateZeroTime", "true");
}

4. MYSQL是如何处理的呢?

通过跟踪代码,数据也是返回了日期类型的,但是rs.getDate使用的是SqlDateValueFactory做了值转换,可以找到这个类,跟踪一下方法.

java 复制代码
public Date localCreateFromDate(InternalDate idate) {
  synchronized (this.cal) {
      try {
          if (idate.isZero()) {
              throw new DataReadException(Messages.getString("ResultSet.InvalidZeroDate"));
          }

          this.cal.clear();
          this.cal.set(idate.getYear(), idate.getMonth() - 1, idate.getDay());
          long ms = this.cal.getTimeInMillis();
          return new Date(ms);
      } catch (IllegalArgumentException e) {
          throw ExceptionFactory.createException(WrongArgumentException.class, e.getMessage(), e);
      }
  }
}

从以上代码可以知道,mysql只赋值了了年月日,对应调试的堆栈信息如下

java 复制代码
SqlDateValueFactory.localCreateFromDate(InternalDate) line: 80	
SqlDateValueFactory.localCreateFromDate(InternalDate) line: 50	
SqlDateValueFactory(AbstractDateTimeValueFactory<T>).createFromDate(InternalDate) line: 67	
SqlDateValueFactory.localCreateFromTimestamp(InternalTimestamp) line: 120	
SqlDateValueFactory.localCreateFromTimestamp(InternalTimestamp) line: 50	
SqlDateValueFactory(AbstractDateTimeValueFactory<T>).createFromTimestamp(InternalTimestamp) line: 87	
MysqlTextValueDecoder.decodeTimestamp(byte[], int, int, ValueFactory<T>) line: 79	
ByteArrayRow(AbstractResultsetRow).decodeAndCreateReturnValue(int, byte[], int, int, ValueFactory<T>) line: 87	
ByteArrayRow(AbstractResultsetRow).getValueFromBytes(int, byte[], int, int, ValueFactory<T>) line: 241	
ByteArrayRow.getValue(int, ValueFactory<T>) line: 91	
ResultSetImpl.getDate(int) line: 740

5. 信创总结

在适配ORACLE数据信创时,由于oracle返回了日期时间的部分.导致迁移到其他数据库时,返回前端的JSON日期串,时间部分丢失了.此时要做信创适配,有一下三种途径

  1. 修改业务代码,需要显示时间的代码,统一调整成rs.getTimestamp
  2. 查看对应数据库驱动,是否有类似oracle.jdbc.DateZeroTime的配置(目前我接触的数据都没有发现)
  3. 修改驱动代码,同样返回时间部分

原文地址:mp.weixin.qq.com/s/0M5nKQSA1...

往期信创ORACEL迁移推荐: 信创-ORACLE迁移到DM8 信创-ORACLE迁移到KingbaseV9

相关推荐
IT_陈寒1 小时前
JavaScript的this又背刺我,这次真长记性了
前端·人工智能·后端
RuoyiOffice2 小时前
SpringBoot+Vue3 企业假期余额系统设计:账户、流水、预占、销假退回与到期清零全链路拆解
spring boot·后端·spring·vue·hr·企业管理软件·ruoyioffice
潘祖记2 小时前
# 一行命令让 AI 接管全屋智能:FeyaGate Skill 保姆级接入教程,小米/涂鸦/美的/易微联全搞定
人工智能·后端·asp.net
小码哥_常12 小时前
告别MySQL!大厂集体转投PostgreSQL,到底藏着什么玄机?
后端
刀法如飞13 小时前
Go数组去重的20种实现方式,AI时代解决问题的不同思路
后端·算法·go
AI人工智能+电脑小能手14 小时前
【大白话说Java面试题】【Java基础篇】第30题:JDK动态代理和CGLIB动态代理有什么区别
java·开发语言·后端·面试·代理模式
swipe14 小时前
别再把 AI 聊天做成纯文本:从 agui 这个前后端项目,拆解“可感知工具调用”的流式 AI UI
后端·langchain·llm
GetcharZp14 小时前
GitHub 爆火!纯 Go 编写的文件同步神器 Syncthing,凭什么成为程序员的标配?
后端
hERS EOUS14 小时前
SpringBoot 使用 spring.profiles.active 来区分不同环境配置
spring boot·后端·spring