jOOQ 转换 DISABLED 得到 null,检查土耳其语 Locale
相同字符串在不同机器上转Boolean结果不同,可能是默认土耳其语环境把大写I改成无点小写。限定3.21.9的文本布尔转换,不改全进程语言来绕过。
相同的字符串 DISABLED,在一台机器上能转成 Boolean.FALSE,换到土耳其语默认环境却得到 null。先检查 Java 默认 Locale,不必先改数据库字符集。
土耳其语规则会把 ASCII 大写 I 转为无点小写 ı。于是 DISABLED 经无参数 toLowerCase() 后变成 dısabled,与布尔词表里的 disabled 不同。
这是转换阶段的语言环境差异
jOOQ 3.21.9 的通用 Boolean 转换路径先执行 from.toString().toLowerCase().trim(),再匹配允许的真假值。无法匹配时,目标为包装类型 Boolean 返回 null,目标为基本类型 boolean 则回落为 false。
因此,基本类型有时会掩盖这个差异:DISABLED 原本也代表假,回落值恰好相同;包装类型或后续拆箱则可能显露为 null 或空指针异常。不要把“基本类型结果看起来正确”当作转换规则没有问题。
排查时保留原字符串、目标类型和默认 Locale 的信息,只输出不含业务敏感值的最小诊断。若输入本来是数据库原生布尔类型,则应检查实际映射链路,不能直接认定它走了字符串词表。
在明确的业务输入边界归一化
如果应用自身接收 ENABLED、DISABLED 两种配置文本,可以在该入口用 Locale.ROOT 处理,并明确拒绝未知值。下面只适用于约定为这两个词的非空输入:
static boolean parseEnabled(String value) {
return switch (value.trim().toLowerCase(java.util.Locale.ROOT)) {
case "enabled" -> true;
case "disabled" -> false;
default -> throw new IllegalArgumentException("Invalid enabled state");
};
}
例子使用支持 switch 表达式的 Java 版本。null 的含义应由调用边界另行定义,不要把缺失配置自动当成关闭;若原业务还接受 yes/no、1/0 等形式,也应按真实契约补齐并验证,不能悄悄缩减允许值。
如果转换发生在 jOOQ 自动映射内部,需要在项目已有的转换器或入参映射边界应用相同规则,不能只改一个独立辅助函数就认为 ORM 已经采用它。
不要在请求处理中调用 Locale.setDefault(...)。那会影响整个进程其他线程的日期、数字和大小写处理,可能把一处读取问题变成全站行为变化。也不要手工替换文本中所有 ı,那会改动本来合法的语言内容。
保留不同 Locale 的回归样本
在隔离环境分别验证普通与土耳其语默认环境下的合法词、未知词、空白和缺失值,核对包装类型与基本类型。业务需要的是一致且明确的输入约定,不是所有输入都勉强变成 false。
该路径已安排到后续维护版本修正,但截至2026年10月25日核对的正式tag仍到3.21.9。升级前应核对正式发行物,不把3.21.10等目标版本当成已经安装的修复。


