You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java向Oracle写入XMLGregorianCalendar时微秒精度丢失问题

XMLGregorianCalendar写入Oracle TIMESTAMP(6)时微秒精度丢失问题排查与解决

问题现象

Java中创建的XMLGregorianCalendar日期对象包含完整微秒(示例:2023-05-24T15:50:07.931456),但通过MyBatis调用存储过程写入Oracle数据库(字段类型为TIMESTAMP(6))后,微秒部分被截断为0(示例:24-MAY-23 15.50.07.931000000 PM),出现精度丢失。

相关代码

Java日期初始化方法

public static XMLGregorianCalendar initializeTime(LocalDateTime localDateTime) {
    try {
        XMLGregorianCalendar time= DatatypeFactory.newInstance()
          .newXMLGregorianCalendar(String.valueOf
          (LocalDateTime.now().truncatedTo(ChronoUnit.MICROS)));
        
    return time;
    } catch (DatatypeConfigurationException var3) {
        throw new RuntimeException("Execution time could not created.");
    }
}

MyBatis调用存储过程代码

<select id="add_time" statementType="CALLABLE" parameterType="java.util.Map">
    {call dummytest(
            #{time, mode=IN, jdbcType=TIMESTAMP}
        )}
</select>

Oracle存储过程代码

create or replace procedure dummytest(p_timestamp TIMESTAMP) is
begin
  insert into testtimestamp values (p_timestamp);
  commit;
end ;

精度丢失原因

  1. MyBatis默认类型处理器的精度限制
    MyBatis自带的XMLGregorianCalendarTypeHandler依赖XMLGregorianCalendar.getTimeInMillis()方法转换日期,该方法仅返回毫秒级时间戳,微秒信息直接被丢弃,导致转换后的JDBC TIMESTAMP仅保留毫秒精度。

  2. XMLGregorianCalendar初始化方式的隐患
    通过LocalDateTime转字符串再创建XMLGregorianCalendar的方式,可能存在解析精度问题:DatatypeFactory.newXMLGregorianCalendar(String)在解析字符串时,未必能正确识别并保留微秒部分的精度,导致XMLGregorianCalendar内部未存储完整的微秒值。

  3. 参数类型精度未明确指定
    存储过程参数仅声明为TIMESTAMP,虽Oracle默认TIMESTAMP精度为6,但MyBatis映射中仅指定jdbcType=TIMESTAMP,未明确精度信息,JDBC驱动可能默认按毫秒精度处理参数,进一步截断微秒。

解决办法

1. 自定义XMLGregorianCalendar类型处理器

实现自定义TypeHandler,手动保留微秒信息转换为JDBC TIMESTAMP:

public class XmlGregorianCalendarTimestampTypeHandler extends BaseTypeHandler<XMLGregorianCalendar> {
    @Override
    public void setNonNullParameter(PreparedStatement ps, int i, XMLGregorianCalendar parameter, JdbcType jdbcType) throws SQLException {
        GregorianCalendar gc = parameter.toGregorianCalendar();
        Timestamp timestamp = new Timestamp(gc.getTimeInMillis());
        // 计算并设置纳秒(微秒转纳秒:1微秒=1000纳秒)
        timestamp.setNanos(parameter.getMillisecond() * 1000000 + parameter.getMicrosecond() * 1000);
        ps.setTimestamp(i, timestamp);
    }

    // 以下方法根据业务需求实现查询时的转换逻辑
    @Override
    public XMLGregorianCalendar getResult(ResultSet rs, String columnName) throws SQLException {
        return null;
    }

    @Override
    public XMLGregorianCalendar getResult(ResultSet rs, int columnIndex) throws SQLException {
        return null;
    }

    @Override
    public XMLGregorianCalendar getResult(CallableStatement cs, int columnIndex) throws SQLException {
        return null;
    }
}

在MyBatis映射中指定该类型处理器:

#{time, mode=IN, jdbcType=TIMESTAMP, typeHandler=com.yourpackage.XmlGregorianCalendarTimestampTypeHandler}

2. 修正XMLGregorianCalendar初始化逻辑

避免字符串转换,直接通过DatatypeFactory API构建对象,确保微秒被正确存储:

public static XMLGregorianCalendar initializeTime(LocalDateTime localDateTime) {
    try {
        LocalDateTime now = LocalDateTime.now().truncatedTo(ChronoUnit.MICROS);
        DatatypeFactory dtf = DatatypeFactory.newInstance();
        XMLGregorianCalendar time = dtf.newXMLGregorianCalendar();
        
        time.setYear(now.getYear());
        time.setMonth(now.getMonthValue());
        time.setDay(now.getDayOfMonth());
        time.setHour(now.getHour());
        time.setMinute(now.getMinute());
        time.setSecond(now.getSecond());
        
        // 拆分纳秒为毫秒和微秒:XMLGregorianCalendar用millisecond存储前3位,microsecond存储后3位
        int nano = now.getNano();
        time.setMillisecond(nano / 1000000);
        time.setMicrosecond((nano % 1000000) / 1000);
        
        return time;
    } catch (DatatypeConfigurationException var3) {
        throw new RuntimeException("Execution time could not created.");
    }
}

3. 明确参数精度

修改存储过程参数,明确指定TIMESTAMP(6)确保精度匹配:

create or replace procedure dummytest(p_timestamp TIMESTAMP(6)) is
begin
  insert into testtimestamp values (p_timestamp);
  commit;
end ;

内容的提问来源于stack exchange,提问作者Yunus Haznedar

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.20 14:13:13