惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

大猫的无限游戏
大猫的无限游戏
H
Hackread – Cybersecurity News, Data Breaches, AI and More
博客园_首页
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
D
Docker
酷 壳 – CoolShell
酷 壳 – CoolShell
宝玉的分享
宝玉的分享
Martin Fowler
Martin Fowler
美团技术团队
量子位
M
MIT News - Artificial intelligence
Apple Machine Learning Research
Apple Machine Learning Research
阮一峰的网络日志
阮一峰的网络日志
博客园 - 叶小钗
博客园 - 三生石上(FineUI控件)
腾讯CDC
Hugging Face - Blog
Hugging Face - Blog
博客园 - 【当耐特】
小众软件
小众软件
博客园 - 司徒正美
罗磊的独立博客
云风的 BLOG
云风的 BLOG
B
Blog RSS Feed
博客园 - 聂微东

博客园_首页

Linux实操--组管理、权限管理和定时任务 Java + EasyExcel 实现单个接口导出多个Excel Mem0 源码解析系列(二):提示词工程的深度剖析 Openclaw TaskFlow究竟是什么?和普通Skill技能有什么区别 博文阅读密码验证 - 博客园 嘉立创开源:应该是全网MicroPython教程最多的开发板 Hermes Agent 集成实践:从协议到生产 2026年AI编程工具横评:Cursor、Codex、Claude Code、Zed、Windsurf Java程序员必看的RAG入门教程 2026 AI效率神器:Superpowers + Claude Code 保姆级教程 本地大模型部署全攻略:从 0 到 1 玩转 Ollama 【从0到1构建一个ClaudeAgent】内存管理-上下文压缩 .NET 高级开发 | 设计、实现一个事件总线框架 电子小白入门之NE555 3. WorkBuddy:隐藏玩法,一键召唤专家,让 AI 以"专家身份"给你干活 和AI一起搞事情#3:Claude Teammate 游戏开发翻车实录 【OpenClaw】通过 Nanobot 源码学习架构---(7)Memory C# .NET 周刊|2026年3月3期 我在 Debian 11 上把 K8s 单机搭起来了,过程没你想的那么顺(/opt 目录版) 深度学习进阶(七)Data-efficient Image Transformer CLI+Skill搭建浏览器AI自动化框架,告别一切重复枯燥任务 告别Token账单无底洞:OpenClaw本地部署,重塑企业数据主权的唯一解 FastAPI+Vue:文件分片上传+秒传+断点续传,这坑我帮你踩平了! SBTI 爆火后,我做了个程序员版的 CBTI。。已开源 + 附开发过程 多模态检索开始进入工程期:用 Sentence Transformers 搭建可落地的 Multimodal RAG 100多行代码实现一个最简单的Agent(用ReAct) Claude Code 通关手册(八):推荐 5 个 Hooks,代码质量提升 3 倍 老板:“有人截图了!”。安全部门:“收到,马上查暗水印!” - why技术 技术之外,皆是人间 C#/.NET/.NET Core技术前沿周刊 | 第 69 期(2026年4.01-4.12)
信创-为什么ORACLE使用JDBC查询SYSDATE时,RS.getDate能获取到...
_herbert · 2026-05-06 · via 博客园_首页

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

1. 这是个什问题?

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

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

✅ [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数据库执行的情况

sysdate()          |
-------------------|
2026-05-03 21:36:24|

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

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

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

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

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

我们看下这部分代码

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 ,这些信息可以通过启动时注入.那我们推测是否是正确的呢?

public static boolean getSystemPropertyDateZeroTime() {
    String str = PhysicalConnection.getSystemPropertyDateZeroTime("false");
    return str.equalsIgnoreCase("true");
}

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

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,对应测试代码如下:

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

4. MYSQL是如何处理的呢?

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

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只赋值了了年月日,对应调试的堆栈信息如下

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. 修改驱动代码,同样返回时间部分

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

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