【问题标题】:Why large number changes value when saved into Oracle DB保存到 Oracle DB 时为什么大数字会改变值
【发布时间】:2014-11-17 06:20:27
【问题描述】:

在一个软件项目中,我们偶然发现了一个错误,我们将 Java 中的一个大数字(17 位)插入到 Oracle DB 过程中。这个数字有时会改变它的值+1或-1。有时保持不变。我们检查了 oracle DB 驱动程序调试日志,发现它打印了正确的数字。 我们还尝试在每个请求之间重新启动 Java 应用程序服务器,以消除奇怪的缓存错误。仍然得到相同的结果。

任何想法为什么会发生这种情况?

代码如下:

// Datasource configuration
<New class="oracle.jdbc.pool.OracleDataSource">
    <Set name="DriverType">thin</Set>
    <Set name="URL">jdbc:oracle:thin:@xxxx</Set>
    <Set name="User">uuuu</Set>
    <Set name="Password">pppp</Set>
</New>

// JAVA
SimpleJdbcCall testMsg = new SimpleJdbcCall(dataSource).
    withSchemaName(schema).
    withCatalogName(catalog).
    withProcedureName("test_msg");

public void testMessage(Long n) {
  testMsg.execute(n, n, n.toString());
}

// PL_SQL
procedure test_msg(
    i           integer,
    n           number,
    v           varchar2
) is
    log_prfx log_pkg.t_log_prfx := 'test_msg: ';
begin
    g_log.log_debug(log_prfx||'i='||to_char(i));
    g_log.log_debug(log_prfx||'n='||to_char(n));
    g_log.log_debug(log_prfx||'v='||v);
end test_msg;

现在打电话

testMessage(10000000000000005L);
testMessage(10000000000000007L);
testMessage(10000000000000009L);

最终得到类似

的日志
test_msg: i=10000000000000005
test_msg: n=10000000000000005
test_msg: v=10000000000000005


test_msg: i=10000000000000008
test_msg: n=10000000000000008
test_msg: v=10000000000000007

test_msg: i=10000000000000008
test_msg: n=10000000000000008
test_msg: v=10000000000000009

我们正在使用的版本。

  • 春季 3.2.11
  • Oracle 驱动程序 ojdbc7_g-12.1.0.2(我们还使用 11.2.0.4 进行了测试)
  • Oracle DB 版本为 12.1.0.1.0
  • Jetty 9.2.2 和 JBoss 7(两者都出现了相同的行为)

【问题讨论】:

  • 我不确定它是否有帮助,但也许你应该使用 BigDecimal 而不是 Long。由于其性质,Oracle 的 NUMBER 对应的类型是 Java 的 BigDecimal。无论如何,您大多使用“数字”作为 ID,您不会将它们用于任何计算。
  • 改了顺序,之前的日志表只是换了个顺序。
  • 稍后我会尝试 BigDecimal 和 BINARY_DOUBLE。会让你知道结果。'
  • 顺便说一句,您在使用 BINARY_DOUBLE 时可能会遇到更多奇怪的问题。该数据库被迁移到 AIX(PowerPC CPU)上的图像。然后您的双精度(Java/Intel)必须转换为不同的表示形式(尽管也符合 IEEE 标准)。 BINARY_DOUBLE 表示运行 DB 服务器的架构的本机浮点表示。

标签: java oracle plsql spring-jdbc ojdbc


【解决方案1】:

您传入的数字大于 oracle 的整数最大值,因此可能会出现不可预知的结果。 oracle 中的最大 int 值为 2147483647。使用正确的号码类型,如in oracle docs 所述

【讨论】:

  • 它没有解释为什么 NUMBER 类型会发生这种情况。还是这样?
  • INTEGER 不是 Oracle 中的原生类型,它只是 NUMBER(38) 的别名。 (在 PL/SQL 中只有一个数据类型 PLS_INTEGER 用于 -2,147,483,648 到 2,147,483,647 范围内的有符号整数,以 32 位表示,但这里不是这种情况。)从浮点表示到数字。按照 Ivan 的建议,在 Java 端尝试使用 BigDecimal 而不是 Long,或者在 PL/SQL 端尝试使用 BINARY_DOUBLE 而不是 NUMBER。
  • 扩展@KimBergHansen 的评论 - IEEE 双精度二进制浮点数的精度限制为 15.95 位十进制数字(即一些 - 甚至大多数)16 位数字可以表示 - 但不是全部 :-) , 但是在 17 位时你已经超过了限制, 是的, 你可以得到像这样的时髦行为。解决方案 - 在你的应用程序和数据库之间来回传递所有内容作为字符串并根据需要进行转换。祝你好运。
  • 抱歉没有行空间。似乎我无法制作那些......我现在做了一些测试。 1) 来自 Java 的 BigDecimal。我无法从 Java 传递它。得到“无效的列类型 binary_double”。我不想惹这个,所以放弃了这个测试。 2) 从 Java 中传入 BigDecimal “整数”和“数字”类型都运行良好 3) 测试 16 位和 17 位数字,来自 Java 16 位的 Long 运行良好。 17 位出现错误。
【解决方案2】:

所以最后我从 Java 中传入 BigDecimal 并在 plsql 中使用 NUMBER(尽管整数也可以)。

感谢 Ivan 的这个提示!

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-12-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多