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

MacOS环境下Oracle插入BigDecimal报ORA-01722错误原因排查

问题描述

我通过getInsertTestDataAfterMigrationCommands()方法向Oracle数据库插入数据时,触发ORA-01722: invalid number错误,移除99.99000的单引号后错误消失。但仅团队MacBook用户会出现此问题,Linux、Windows设备及Jenkins流水线均无异常;且仅BigDecimal类型值带单引号时出错,其他字段正常。已关闭MacBook智能引号功能,错误仍存在。

相关代码及错误信息如下:

实体类

@Entity
@Table(name = "policy_restriction_bound",
    uniqueConstraints = @UniqueConstraint(columnNames = {"policy_restriction_id", "upperBound", "currency"},
        name = "uq_policy_restriction_bound_restr_id_bound_currency"))
public class PolicyRestrictionBoundEntity {
    public static final int UPPER_BOUND_SCALE = 5;

    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "sequence-generator")
    @SequenceGenerator(name = "sequence-generator", sequenceName = "hibernate_sequence", allocationSize = 5)
    private Long id;

    @NotNull
    @ManyToOne
    @JoinColumn(name = "policy_restriction_id", foreignKey = @ForeignKey(name = "fk_policy_restr_bound2policy_restr"))
    private PolicyRestrictionEntity policyRestriction;

    @NotNull
    private Integer minimumApprovesCount;

    @Positive
    @Access(AccessType.PROPERTY)
    private BigDecimal upperBound;

    @Size(min = 1, max = 3)
    private String currency;
}

容器配置

private static final OracleContainer DB_CONTAINER = new OracleContainer("gvenzl/oracle-xe:18.4.0-slim-faststart");

插入语句

protected List<String> getInsertTestDataAfterMigrationCommands() {
        return List.of("""
                INSERT into policy_restriction_bound(id, policy_restriction_id, minimum_approves_count, upper_bound, currency)
                VALUES ('1', '1', '2', '99.99000', 'USD')"""
        );
    }

错误信息

java.lang.RuntimeException: java.sql.SQLSyntaxErrorException: ORA-01722: invalid number
Caused by: Error : 1722, Position : 134, Sql = INSERT into policy_restriction_bound(id, policy_restriction_id, minimum_approves_count, upper_bound, currency)
VALUES ('1', '1', '2', '99.99000', 'USD'), OriginalSql = INSERT into policy_restriction_bound(id, policy_restriction_id, minimum_approves_count, upper_bound, currency)
VALUES ('1', '1', '2', '99.99000', 'USD'), Error Msg = ORA-01722: invalid number

    at oracle.jdbc.driver.T4CTTIoer11.processError(T4CTTIoer11.java:637)
可能的原因及分析
  • 系统区域设置差异:
    MacBook的默认区域设置可能使用逗号作为小数分隔符(例如部分欧洲地区或特殊中文区域配置),Oracle JDBC驱动会根据客户端区域自动设置会话的NLS_NUMERIC_CHARACTERS参数。当该参数值为,.(逗号是小数分隔符、点是千分符)时,字符串'99.99000'会被Oracle判定为包含无效千分符的数值,触发解析错误。而Linux、Windows及Jenkins的默认区域通常使用点作为小数分隔符,因此不会出现问题。

  • JVM默认Locale差异:
    MacBook上的JVM默认Locale可能与其他系统不一致,导致JDBC驱动处理字符串转数值时采用了不同规则。例如JVM默认Locale为fr_FR(法语区)时,小数分隔符是逗号,驱动会按此规则处理字符串,进而导致数据库解析失败。

  • JDBC驱动OS适配逻辑差异:
    不同操作系统下,Oracle JDBC驱动读取客户端区域配置的逻辑存在细微差别。MacOS下驱动可能优先读取系统级区域设置,而Linux/Windows优先使用JVM的Locale设置,这种差异导致仅Mac环境出现解析错误。

验证方法
  1. 在Mac环境执行SQL查询当前会话的NLS参数:

    SELECT PARAMETER, VALUE FROM NLS_SESSION_PARAMETERS WHERE PARAMETER = 'NLS_NUMERIC_CHARACTERS';
    

    若返回VALUE为,.,则说明小数分隔符设置是问题根源。

  2. 在代码中打印当前JVM的Locale:

    System.out.println(Locale.getDefault());
    

    对比Linux/Windows环境的输出,确认是否存在Locale差异。

解决方案
  1. 规范SQL写法(推荐):
    数值类型字段的插入值不应使用单引号包裹,直接写数值即可:

    INSERT into policy_restriction_bound(id, policy_restriction_id, minimum_approves_count, upper_bound, currency)
    VALUES (1, 1, 2, 99.99000, 'USD')
    

    这是最根本的解决方式,符合SQL语法规范,彻底避免类型转换问题。

  2. 强制指定数据库会话数值格式:
    在JDBC连接URL中添加参数,强制使用点作为小数分隔符:

    jdbc:oracle:thin:@//host:port/service?NLS_NUMERIC_CHARACTERS=.,
    
  3. 统一JVM的Locale设置:
    应用启动时添加JVM参数,指定默认Locale为使用点作为小数分隔符的区域:

    -Duser.language=en -Duser.region=US
    
  4. 使用TO_NUMBER函数显式指定格式:
    若必须保留单引号(不推荐),可在SQL中用TO_NUMBER指定解析格式:

    INSERT into policy_restriction_bound(id, policy_restriction_id, minimum_approves_count, upper_bound, currency)
    VALUES ('1', '1', '2', TO_NUMBER('99.99000', '99999.99999'), 'USD')
    

内容的提问来源于stack exchange,提问作者Yusuf BESTAS

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.10 13:47:44