Oracle 将空字符串视为 Java / JPA 程序员的 NULL 问题

Posted

技术标签:

【中文标题】Oracle 将空字符串视为 Java / JPA 程序员的 NULL 问题【英文标题】:Oracle treating empty string as NULL problem for a Java / JPA programmer 【发布时间】:2011-07-27 12:00:04 【问题描述】:

Oracle 将空字符串作为 null 存储在数据库中的情况如何处理?

我希望它存储为空字符串,因为它不是 NULL,因为发出查询会更容易。

这样的事情会选择空字符串和非空字符串,但不会选择空值

select * from mytable where myfield like '%';

如果我还想选择空值(最初应该是空字符串),我必须像这样选择:

select * from mytable where myfield like '%' or myfield is null;

我很想在以后的 sql 语句中跳过or myfield is null

我想到的当前解决方案是在应用程序级别处理这个问题, 例如,在实体中,我将我的所有 String 字段默认值初始化为一个空格,例如:

@Entity
public class MyEntity 
  private String name = " ";

  public void setName(String name) 
    if (isEmptyString(name)) 
      name = " ";
    
  
  ...


或者,也许,我可以使用 Oracle 11g 中我仍然未知的新类型,它可以保持空字符串不变而不将其更改为空值?

谢谢!

【问题讨论】:

我想您可以使用@PrePersist 并检查名称字段的大小,如果为空,则将其替换为“”。 我通过 not 使用存在根本缺陷的 DBMS 来解决该特定问题。换句话说,我使用 DB2 :-) 一些 JPA 实现可能会通过插入一个特殊字符来表示空字符串来满足这一点。这就是我们使用 DataNucleus 为您做的事情 来自 Oracle 官方文档:“Oracle 数据库目前将长度为零的字符值视为 null。但是,在未来的版本中可能不会继续如此,Oracle 建议您不要这样做将空字符串视为与 null 相同。” docs.oracle.com/cd/B28359_01/server.111/b28286/… 这听起来像 NULL 和 "" 总是被认为是相等的 - 但是您(和我)观察到 "" 只是存储为 NULL,因此与 "" 进行比较可能会产生不同的结果,对吗? 【参考方案1】:

相信 Oracle 正在通过将空字符串转换为 NULL 来优化数据库。 首先,可能仍需要在数据块级别 (*1) 将空字符串显式存储在数据库存储中。存储为 NULL 可以减少数据存储空间。 其次,如果在列上定义了任何索引,则 NULL 值不包含在索引中,从而减少了索引存储空间。 (*2)

从设计和开发的角度来看,除非空白空间真的从业务角度改变了数据的语义,否则存储为 NULL 应该没问题。

按照上面的建议在框架级别(或父类)添加代码将消除在所有子类和对象中输入代码的需要 - 这就是面向对象甚至基本编程努力做的事情。

如果我的代码性能最佳是因为我的数据库存储得到了优化,那么我应该很高兴。 如果我的代码性能在生产中下降只是因为我想节省一点打字或未能进行精湛的设计/开发,这很糟糕?

我很高兴 Oracle 在幕后为我优化它。

关于 Oracle NULL:

NULL 是一个特殊值。 NULL 不等于包括自身在内的任何事物,即 NULL 不等于 NULL

【讨论】:

“除非空白空间真的改变了数据的语义”:确实如此!然而,在几乎所有应用中,这种差异并不经常对少数领域很重要。大多数语言中存在差异并不是没有原因的。数据库不应阻止这种区别。如果甲骨文“优化”是一个不错的选择,但它不提供选择。甚至甲骨文也知道他们错了,正如@RobertG 指出的docs.oracle.com/cd/B28359_01/server.111/b28286/… :“这在未来的版本中可能不会继续存在”【参考方案2】:

不仅是特殊where条件的选择,还有Java字符串对象的处理。 如果你有一个 String a="" 你可以调用它的 length 方法并得到 0。 如果你有一个 String a=null ,那么在调用 length 时你会得到一个空指针异常。 因此,使用 oracle 数据库会迫使您在检查长度之前始终检查您的字符串是否为空:(

【讨论】:

【参考方案3】:

对我来说还早,但不是

select * from mytable where myfield like '%' or myfield is null

select * from mytable

所以,Oracle 简化了您的生活! ;)

【讨论】:

对不起这个简单的例子,但想象一下这种情况: select * from mytable where myfield1 like '%' and myfield2 = 'xxx';任何值不为 null 的 myfield1 都会显示。但是,如果 Oracle 能够存储空字符串,则这些值将匹配并显示出来。也许问题是为什么我使用 like '%' 我可以跳过它。因为有时过滤器值可能会根据用户输入是动态的,如果它是空的,我通常用 % 替换它,并且会有 like '%' 如果用户输入a 'abc',然后它变成 like '%abc%' @Albert,我仍然很困惑,请原谅我厚厚的脑袋 ;) 你什么时候需要这个 => 和 myfield1 像 '%' where myfield1 like '%abc%' 【参考方案4】:

是的,这就是 Oracle 的运作方式。空字符串被视为空值。

您当然可以在应用程序级别“修复”这个问题 - 例如,按照您的建议存储 " " 值 - 但首先考虑一下,与 NULL 值相比,您的“空字符串”值到底有什么区别?为什么你需要区别对待它们?我曾经也遇到过这种困境,但通常发现很少有我真正需要区分的情况。

【讨论】:

mi:在应用程序级别对我来说不是问题。但是在 sql 级别,该语句在尝试获取数据时会充满检查 nullity,因为就像我上面的示例一样,如果程序员忘记添加检查是否为 null,则不会检索数据。如果 Oracle 将其保留为空字符串,则不需要这样做。 但是如果你的列是可以为空的,那不就是你必须忍受的吗?如果你想获取行where myfield = 'X',并且还包括myfield为空的行,你只需要添加or myfield is null。没有办法解决这个问题,但我真的不明白这有什么问题。 mi:假设在 postgresql 中,myfield 有 'hello' 和 '',并且使用 where myfield like '%' 执行查询会显示两者。但是在 Oracle 中,当 myfield 有 'hello' 和 '' 时,使用 where myfield like '%' 执行查询只会显示一个。而 '' 对我们来说真的很有意义,因为它被视为一种价值。 如果您确实需要将'' 值与“真实”NULL 值分开,恐怕您必须自己实施解决方案。 (正如您自己所说,将空字符串转换为 " " 是一个流行的选项。)对此感到抱歉。但我仍然会强烈考虑这是否真的需要。 检索语言对它们的处理方式明显不同。 NULL 字符串对象(未实例化的对象,即NULL)与空字符串对象(已实例化,值为'')不同。作用于对象的代码通常期望它被实例化;如果对意外未实例化的对象(NULL)进行操作,则所述代码将引发 Null 指针异常。作为一个案例示例,发布到视图的数据可能会以不同于空字符串的方式处理 NULL。由非 Oracle 数据库 (MSSQL) 支持的应用程序代码在迁移到 Oracle 时需要添加额外的空验证。【参考方案5】:

不,没有办法将空字符串视为空字符串。 Oracle 总是将长度为零的字符串视为 NULL 值。

【讨论】:

【参考方案6】:

试试

create index idx_myfield on mytable(nvl(myfield,-1));

select * from mytable where nvl(myfield,-1)=-1;

【讨论】:

以上是关于Oracle 将空字符串视为 Java / JPA 程序员的 NULL 问题的主要内容,如果未能解决你的问题,请参考以下文章

如何正确地转换成 java.util.Date 通过 JPA 的 Oracle 日期字段

oracle存空字符串怎么会变成null

将ORACLE中查询结果为空字符串的变成null该怎么做

使用 JPA 和 Hibernate 将 Java 布尔值映射到 Oracle Number 列

Oracle 将 "'SParis2" 的文本解析为字符串文字,然后找到 S 将其视为无法识别的 SQL 转义序列

hive的空字符串与null