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环境出现解析错误。
在Mac环境执行SQL查询当前会话的NLS参数:
SELECT PARAMETER, VALUE FROM NLS_SESSION_PARAMETERS WHERE PARAMETER = 'NLS_NUMERIC_CHARACTERS';若返回
VALUE为,.,则说明小数分隔符设置是问题根源。在代码中打印当前JVM的Locale:
System.out.println(Locale.getDefault());对比Linux/Windows环境的输出,确认是否存在Locale差异。
规范SQL写法(推荐):
数值类型字段的插入值不应使用单引号包裹,直接写数值即可:INSERT into policy_restriction_bound(id, policy_restriction_id, minimum_approves_count, upper_bound, currency) VALUES (1, 1, 2, 99.99000, 'USD')这是最根本的解决方式,符合SQL语法规范,彻底避免类型转换问题。
强制指定数据库会话数值格式:
在JDBC连接URL中添加参数,强制使用点作为小数分隔符:jdbc:oracle:thin:@//host:port/service?NLS_NUMERIC_CHARACTERS=.,统一JVM的Locale设置:
应用启动时添加JVM参数,指定默认Locale为使用点作为小数分隔符的区域:-Duser.language=en -Duser.region=US使用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

