oracle - 使用 Types.TIME 将 java.time.LocalTime 保留到 oracle 中会丢失小数秒精度

标签 oracle jdbc java-time localtime jsr310

我对 Oracle 有一种行为,我不确定这是有意还是无意。

我正在研究各种类型的 java 日期/时间 API,在 JDBC 级别使用它们。

我正在尝试将 java.time.LocalTime 的实例持久保存到 Oracle 数据库中。

在 JDBC 级别,我的理解是我应该使用:

  • 兼容 JDBC 4.2 的驱动程序
  • PreparedStatement.setObject(int parameterIndex, Object x, int targetSqlType)
  • Types.TIME 作为 java.time.LocalTime 实例的 targetSqlType

由于oracle不提供TIME类型,所以我使用TIMESTAMP字段。

问题是,如果我在 JDBC 级别使用 Types.TIME,我就会丢失小数秒精度。

如果我使用 Types.TIMESTAMP,精度将保持不变。

如果可能的话,我想对 LocalTime 使用 JDBC 预期的标准映射。

这里的测试显示了行为:

@Test
public void testSetObjectWithSqlTypeTimestamp() {
    testSetObjectWithSqlType("java.sql.Types.TIMESTAMP", Types.TIMESTAMP);
}

@Test
public void testSetObjectWithSqlTypeTime() {
    testSetObjectWithSqlType("java.sql.Types.TIME", Types.TIME);
}

private void testSetObjectWithSqlType(String sqlTypeLabel, Integer sqlTypeValue) {
    logger.info("Testing setObject with {} (value={})", sqlTypeLabel, sqlTypeValue);

    LocalTime willBeInserted = LocalTime.of(21, 17, 23, 678_987_000);

    Connection con = null;
    PreparedStatement ps = null;
    try {
        con = dataSource.getConnection();
        con.setAutoCommit(false);
        ps = con.prepareStatement("insert into LOCAL_TIME_TABLE (FOO) values (?)");
        ps.setObject(1, willBeInserted, sqlTypeValue);

        ps.executeUpdate();
        con.commit();
    } catch (SQLException e) {
        rollbackQuietly(con);
        throw new RuntimeException("Error inserting test value", e);
    } finally {
        JdbcUtils.closeStatement(ps);
        JdbcUtils.closeConnection(con);
    }

    List<LocalTime> results = jdbcTemplate.queryForList("select FOO from LOCAL_TIME_TABLE", LocalTime.class);
    assertThat(results)
        .containsExactlyInAnyOrder(willBeInserted)
        .hasSize(1);
}

Types.TIMESTAMP 测试工作正常。

Types.TIME 测试出现以下错误:

 [ERROR] Failures: 
 [ERROR]   LocalTimeSetObjectWithSqlTypeOracleBugTest.testSetObjectWithSqlTypeTime:44->testSetObjectWithSqlType:72 
 Expecting:
   <[21:17:23]>
 to contain exactly in any order:
   <[21:17:23.678987]>
 elements not found:
   <[21:17:23.678987]>
 and elements not expected:
   <[21:17:23]>

这是一个spring-boot测试(DataSource和JdbcTemplate由spring提供)。 我没有详细说明所有 Spring 样板代码,这似乎与问题无关;但如果需要的话我会添加它。

我的 FOO 表如下所示:

create table LOCAL_TIME_TABLE (
    FOO timestamp
);

我正在使用以下 Oracle 镜像:https://hub.docker.com/r/wnameless/oracle-xe-11g-r2

以及以下驱动程序(maven 坐标):com.oracle.database.jdbc:ojdbc10:19.3.0.0

所以我想我的问题有两个:

  • 我假设 LocalTime 的预期标准 JDBC 映射是: ps.setObject(1, localTime, Types.TIME) 是否正确?如果不是,那么预期的 JDBC 方式是什么?
  • Oracle 的行为是有意为之吗?如果是,是否记录在某处?

此外,这是我的第一个 SO 问题,因此欢迎任何反馈。

祝你编码愉快!

最佳答案

Oracle 数据库以 Oracle SQL DATE 格式存储 TIME 值。 Oracle SQL DATE 不存储秒的小数部分。如果秒小数部分很重要,则使用 JDBCType.TIMESTAMP。

SQL 规范很微妙(轻描淡写)。所有供应商的 SQL 实现都略有不同。 SQL 规范足够灵活,所有主要 SQL 数据库都符合要求,其中包括 Oracle 数据库。供应商的数据库之间存在显着差异,随意阅读 SQL 规范可能会产生误导。 Oracle 数据库允许将 TIME 表示为 DATE,因为不存储小数秒。

编辑添加:https://docs.oracle.com/en/database/oracle/oracle-database/19/sqlrf/Data-Types.html#GUID-7B72E154-677A-4342-A1EA-C74C1EA928E6

关于oracle - 使用 Types.TIME 将 java.time.LocalTime 保留到 oracle 中会丢失小数秒精度,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61840178/

相关文章:

java - 哪些数据库可以与java一起使用?

java - 有没有办法在构造后确定 DateTimeFormatter 是仅日期还是仅时间?

sql - 许多天之间的人类可读的耗时

sql - 如何优化在具有700M行的Oracle表上运行的更新SQL

java - 将数据库从 ms access 转换为 mysql 后如何运行 Java 项目

mysql - 每30秒通过JDBC向mysql发送纬度、经度

Java8 DateTimeFormatter 解析具有个位数和双位数日期的日期

java - 使用 Java 8 DateTimeFormatter 将字符串转换为 LocalDateTime

java - 当我有 PIVOT 时,使用 JPA 如何在 native 查询中设置参数

java - 如何部署 ADF 业务组件?