Oracle中空字符串""与NULL本质不同,但数据库将其统一存储为NULL;JDBC写入时""自动转NULL,读取时getString()均返回null,需用wasNull()区分,业务层须通过约定或额外字段明确语义。

Oracle里空字符串和NULL真不一样
Java应用连Oracle时,""(空字符串)会被JDBC驱动自动转成NULL写入,但读出来时,NULL和""在数据库层面是两个值——Oracle把空字符串当成NULL存储,但某些场景(比如触发器、物化视图、DBA直接查DUMP())能区分它们。更麻烦的是:JDBC默认行为让Java层“看不见”这个差异,你以为写了"",实际存的是NULL;反过来,从DB读到NULL,你无法判断它原本是NULL还是""。
用setString()配合setNull()显式控制写入
别依赖PreparedStatement.setString(1, "")——它会把空字符串当作NULL发给Oracle。要保留语义,就得手动区分:
- 想存真正的
NULL:用ps.setNull(1, Types.VARCHAR) - 想存空字符串(虽Oracle内部仍转为
NULL,但业务逻辑需标记为“有意留空”):先检查字段是否允许为空,再考虑用ps.setString(1, ""),但必须配套读取时的约定 - 如果表字段定义为
NOT NULL,又想表达“未填写”,建议改用特殊占位符如"<unset>"</unset>或"<empty>"</empty>,避免和NULL混淆
读取时用wasNull()判断原始NULL,再结合业务规则补全语义
ResultSet.getString()对NULL和空字符串都返回null,所以不能只靠返回值。正确姿势是:
- 调用
rs.getString("col")后,立刻跟一句rs.wasNull() - 如果
wasNull()返回true,说明数据库返回的是NULL(不是空字符串) - 如果
wasNull()返回false但结果是"",那才是Oracle存的空字符串(极少见,通常只出现在用EMPTY_CLOB()或EMPTY_BLOB()等特殊处理路径) - 更稳妥的做法:在DAO层统一包装,例如定义
Optional<string></string>,empty()表示NULL,of("")表示明确的空字符串(靠上游写入约定保证)
Spring JDBC里用SqlTypeValue绕过默认转换
如果你用JdbcTemplate或NamedParameterJdbcTemplate,可以自定义类型绑定来规避"" → NULL的隐式转换:
立即学习“Java免费学习笔记(深入)”;
new SqlTypeValue() {
@Override
public void setTypeValue(PreparedStatement ps, int paramIndex, int sqlType, String typeName) throws SQLException {
if (value == null) {
ps.setNull(paramIndex, sqlType);
} else if (value.isEmpty()) {
// 强制写入空字符串(实际仍被Oracle转NULL,但语义明确)
ps.setString(paramIndex, "");
} else {
ps.setString(paramIndex, value);
}
}
}
注意:这不能改变Oracle底层行为,只是让Java层的意图更清晰。真正要隔离语义,还得靠字段设计(比如加is_empty_flag CHAR(1))或应用层协议约束。
最易被忽略的一点:Oracle 23c开始支持EMPTY_STRING配置项,但需要服务端开启且JDBC驱动版本≥21,老项目基本不可用。所以现实方案永远是——接受Oracle把""当NULL,然后在Java里用额外字段或上下文约定去承载“空字符串”的业务含义。


















