【问题标题】:Checking for a null int value from a Java ResultSet从 Java ResultSet 检查 null int 值
【发布时间】:2011-02-24 14:37:44
【问题描述】:

在 Java 中,我试图从 ResultSet 中测试空值,其中列被强制转换为原始 int 类型。

int iVal;
ResultSet rs = magicallyAppearingStmt.executeQuery(query);
if (rs.next()) {
  if (rs.getObject("ID_PARENT") != null && !rs.wasNull()) {
    iVal = rs.getInt("ID_PARENT");
  }
}

从上面的代码片段中,有没有更好的方法来做到这一点,我假设第二个wasNull() 测试是多余的?

教育我们,谢谢

【问题讨论】:

  • 我发现了这个问题,因为我在数据库中有一个可为空的列,并且它在 Java 中由整数表示。您会认为在数据库中有一个可以为空的数值列会很常见,结果集 API 会更优雅地适应它。
  • 我不会将此作为答案发布,因为它是切线的,远非普遍:我通常的解决方案是将IF(colName = NULL, 0, colName) AS colName 放在SELECT 语句中(最好在存储过程中)。从哲学上讲,这归结为数据库是否应该符合应用程序,反之亦然。由于 SQL 可以轻松处理 NULL,而许多 SQL 使用者却不能(即java.sql.ResultSet),因此我选择尽可能在数据库中处理它。 (当然,这假设在概念上 NULL 和零对于您的目的是等效的。)
  • 您可以在 SQL 中轻松完成此操作,而无需担心 SQL NULL 和 Java null。只需编写 ... IS NOT NULL 并将其作为 JDBC 中的 boolean 值检索。

标签: java null resultset


【解决方案1】:

如果您想要调用 ResultSet.wasNull() 的替代方法,您可以使用 getObject() 并转换为正确的类型。

Long val = (Long)rs.getObject(pos++);

您还可以在StatementsetObject() 中设置空值。

pstmt.setObject(pos++, null);

【讨论】:

    【解决方案2】:

    以防万一有人在 Kotlin 编程时来到这里(就像我一样),BalusC 建议的答案可以正常工作。请注意ShortFloatResultSet 中分别被实例化为IntegerDouble,我们应该在调用getObject() 之后将它们转换为正确的类型。就我而言,最终代码是:

    when {
        propKClass.isSubclassOf(Int::class) -> rs.getObject(colName) as Int? 
        propKClass.isSubclassOf(Short::class) -> (rs.getObject(colName) as Int?)?.toShort()
        propKClass.isSubclassOf(Long::class) -> rs.getObject(colName) as Long?
        propKClass.isSubclassOf(Boolean::class) -> rs.getObject(colName) as Boolean?
        propKClass.isSubclassOf(Double::class) -> rs.getObject(colName) as Double?
        propKClass.isSubclassOf(Float::class) -> (rs.getObject(colName) as Double?)?.toFloat()
        else -> rs.getString(colName)
    }
    

    【讨论】:

      【解决方案3】:

      您可以使用 resultSet 和具有 Number 类型的列名来调用此方法。它将返回整数值或 null。数据库中的空值不会返回零

      private Integer getIntWithNullCheck(ResultSet rset, String columnName) {
          try {
              Integer value = rset.getInt(columnName);
              return rset.wasNull() ? null : value;
          } catch (Exception e) {
              return null;
          }
      }
      

      【讨论】:

      • 您能否详细说明这是如何解决问题的?
      • 您可以使用 resultSet 和 Number 类型的列名来调用此方法。它将返回整数值或 null。数据库中的空值不会返回零。
      • 太棒了!将其编辑到您的答案中(并在编辑后删除评论),您就有了一个很好的答案:)
      【解决方案4】:

      只是 Java 泛型的更新。

      您可以创建一个实用方法来从给定的 ResultSet 中检索任何 Java 类型的可选值,之前已转换。

      不幸的是,getObject(columnName, Class) 不返回 null,而是给定 Java 类型的默认值,因此需要 2 次调用

      public <T> T getOptionalValue(final ResultSet rs, final String columnName, final Class<T> clazz) throws SQLException {
          final T value = rs.getObject(columnName, clazz);
          return rs.wasNull() ? null : value;
      }
      

      在本例中,您的代码可能如下所示:

      final Integer columnValue = getOptionalValue(rs, Integer.class);
      if (columnValue == null) {
          //null handling
      } else {
          //use int value of columnValue with autoboxing
      }
      

      很高兴得到反馈

      【讨论】:

      • 有趣。如果您调用getObject(),则返回一个已经可以回答空值的引用类型。 v11 的 Javadoc 说它为 SQL NULLs 回答 null。除非您有不寻常的驱动程序或数据类型来回答某种非空“空”对象,否则我希望 wasNull() 只有在读取整数或其他原始类型后才有用,无论返回类型不能直接表示空。跨度>
      【解决方案5】:

      当字段值为NULLResultSet.getInt 的默认值是返回0,这也是iVal 声明的默认值。在这种情况下,您的测试完全是多余的。

      如果你真的想在字段值为NULL的情况下做一些不同的事情,我建议:

      int iVal = 0;
      ResultSet rs = magicallyAppearingStmt.executeQuery(query);
      if (rs.next()) {
          iVal = rs.getInt("ID_PARENT");
          if (rs.wasNull()) {
              // handle NULL field value
          }
      }
      

      (以下编辑为@martin cmets;所写的OP代码无法编译,因为iVal未初始化)

      【讨论】:

      • @Roman - 查看 ResultSet 中 getInt 的 javadoc:“返回:列值;如果值为 SQL NULL,则返回值为 0”
      • 事实确实很荒谬。 getInt() 应该是 getInteger(),如果 DB 值为 null,则返回 Integer,即 null。开发人员真的把这个搞砸了。
      • @sactiw 按照这个逻辑,java 上的一切都应该被开发来避免 NPE,但事实并非如此。避免 NPE 是应用程序开发人员的责任,而不是语言内部 API 的责任。
      • OMG 为什么这样做,根本不返回 NULL,0NULL 是两个大不同的东西
      • @ryvantage ResultSet 是原始 Java 类之一,当时 Java 不支持自动拆箱。此外,它是一个非常低级的 API,不应强制将来自数据库的原始字节包装在 Java Integer 对象中的潜在额外成本。有一个参数可能用于将 getInteger 添加到返回 Integer(甚至 Optional)的接口。
      【解决方案6】:

      为方便起见,您可以围绕 ResultSet 创建一个包装类,当 ResultSet 通常不会返回 null 值。

      public final class ResultSetWrapper {
      
          private final ResultSet rs;
      
          public ResultSetWrapper(ResultSet rs) {
              this.rs = rs;
          }
      
          public ResultSet getResultSet() {
              return rs;
          }
      
          public Boolean getBoolean(String label) throws SQLException {
              final boolean b = rs.getBoolean(label);
              if (rs.wasNull()) {
                  return null;
              }
              return b;
          }
      
          public Byte getByte(String label) throws SQLException {
              final byte b = rs.getByte(label);
              if (rs.wasNull()) {
                  return null;
              }
              return b;
          }
      
          // ...
      
      }
      

      【讨论】:

        【解决方案7】:

        使用 java 8 你可以做到这一点:

        Long nVal = Optional.ofNullable(resultSet.getBigDecimal("col_name"))
                            .map(BigDecimal::longValue).orElse(null));
        

        在这种情况下,如果 SQL 值为 NULL,请确保 nVal 为空(而不是零)

        【讨论】:

        • 但不适用于resultSet.getInt("col_name")
        • 使用 MSSQL 数据源,这似乎不起作用。需要额外检查if (rs.wasNull())
        【解决方案8】:

        只需检查该字段是否为null 或不使用ResultSet#getObject()。用你想要的任何空值替换-1

        int foo = resultSet.getObject("foo") != null ? resultSet.getInt("foo") : -1;
        

        或者,如果您可以保证使用正确的 DB 列类型,以便 ResultSet#getObject() 真正返回 Integer(因此不是 LongShortByte),那么您也可以只将其类型转换为 Integer

        Integer foo = (Integer) resultSet.getObject("foo");
        

        【讨论】:

        • 否,但它会在非空情况下构造一个不必要的 Integer 对象。 (顺便说一句,大多数 JDBC 驱动程序根本不会在任何 ResultSet 方法调用期间访问数据库……通常,在所有数据都通过网络传输之前,您不会得到 ResultSet)。
        • 这取决于获取大小。在大多数驱动程序中,默认值为 10 行,一旦获取到,它们将被处理,但在处理完成之前不会获取下一次获取。
        • 我很惊讶你的答案不是更好的答案 :-) 我投了赞成票,因为它是避免非常棘手的不安全 wasNull() 方法的正确答案。对我来说,停止使用 Java 是一个补充理由 ;-) 并继续使用 VB.Net,其中 RecordSet 解决了这个简单的问题 10 多年!
        【解决方案9】:

        另一种解决方案:

        public class DaoTools {
            static public Integer getInteger(ResultSet rs, String strColName) throws SQLException {
                int nValue = rs.getInt(strColName);
                return rs.wasNull() ? null : nValue;
            }
        }
        

        【讨论】:

          【解决方案10】:

          我认为,这是多余的。 rs.getObject("ID_PARENT") 应该返回 Integer 对象或 null,如果列值实际上是 NULL。所以它甚至应该可以做这样的事情:

          if (rs.next()) {
            Integer idParent = (Integer) rs.getObject("ID_PARENT");
            if (idParent != null) {
              iVal = idParent; // works for Java 1.5+
            } else {
              // handle this case
            }      
          }
          

          【讨论】:

          • 嗯,至少在我的情况下,问题在于调用 getObject 不一定返回整数,因为我正在使用的 oracle db 中的列类型的性质(“数字”)。
          • Matt 的同样问题... 对于 MySQL 和 Types.BIGINT(应该映射到 Long),getObject() 方法返回 0 而不是 null。
          • 也可以rs.getObject("ID_PARENT", Integer.class)
          【解决方案11】:

          如果您可以控制 SQL,另一种很好的检查方法是在查询本身中为您的 int 列添加一个默认值。然后检查该值。

          例如,对于 Oracle 数据库,使用 NVL

          SELECT NVL(ID_PARENT, -999) FROM TABLE_NAME;
          

          然后检查

          if (rs.getInt('ID_PARENT') != -999)
          {
          }
          

          当然,这也是假设存在一个通常不会在列中找到的值。

          【讨论】:

          • 我否决了这个答案,因为它很可能会给很多人带来问题。如果定义为可为空,则 int 列具有一组由正数、零、负数和 NULL 组成的值。在任何时候都可以简单地插入包含这个幻数的有效数据行,突然间事情就会变糟。它基本上是幻数反模式的实现。不要这样做。
          【解决方案12】:

          AFAIK 你可以简单地使用

          iVal = rs.getInt("ID_PARENT");
          if (rs.wasNull()) {
            // do somthing interesting to handle this situation
          }
          

          即使它是 NULL。

          【讨论】:

            猜你喜欢
            • 2011-02-26
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多