Java和SQL:返回null或抛出异常?

时间:2009-10-09 16:57:34

标签: java mysql exception layer

这是另一个有争议的主题,但这一次我只搜索简单而有记录的答案。场景:

我们假设以下方法:

 public static Hashtable<Long, Dog> getSomeDogs(String colName, String colValue) {
  Hashtable<Long, Dog> result = new Hashtable<Long, Dog>();
  StringBuffer sql = null;
  Dog dog = null;
  ResultSet rs = null;
      try {
          sql = new StringBuffer();
          sql.append("SELECT * FROM ").append("dogs_table");
          sql.append(" WHERE ").append(colName).append("='");
          sql.append(colValue).append("'");
          rs = executeQuery(sql.toString());
              while (rs.next()) {
                  dog= new Dog();
                  //...initialize the dog from the current resultSet row
              result.put(new Long(dog.getId()), dog);
              }
          }
     catch (Exception e) {
         createErrorMsg(e);
         result = null; //i wonder....
         }
     finally {
         closeResultSet(rs); //this method tests for null rs and other stuff when closing the rs.
     }
   return result;
 }

问题:

1。您有什么方法可以改善这种归还某些狗的技术,具有一些属性?

2。 rs.next()将为null ResultSet返回false,或者将生成异常,如下所示:

String str = null; 的System.out.println(str.toString());

3。如果在从ResultSet的当前行初始化dog对象时发生了一些不好的事情,例如:连接失败,不兼容的值已经传递给dog属性设置器等等,该怎么办?我现在可能在哈希表中有10个元素,或者没有(第一行)。下一步操作是什么:a)返回空哈希表; b)返回结果哈希表,它在这个阶段的方式; c)抛出异常:这里的异常类型是什么?

4。我想你们都会同意这一点:没有什么不好的事情发生,审讯中没有行,空值就会返回。但是,@ThorbjørnRavnAndersen说here我应该返回一个NullObject而不是null值。我不知道那是什么。

5。我注意到人们和一群人说应该将应用程序拆分成层或某种层次。考虑到上面的例子,这里有哪些层,除了我能想到的这些层:

Layer1 :: Database层,执行操作:此方法。

Layer2 :: ??? :构建新Dog对象的一些层:我的Dog对象。

Layer3 ::? :我打算用狗的集合做一些事情的层:主要是GUI层,或用户界面的子层。

在应用程序流程之后,如果在第一层中发生异常,最好的想法是什么?我的想法:捕获异常,记录异常,返回一些值。这是最好的做法吗?

Manny感谢您的回答,我期待着看到其他人对这些问题的看法。

6 个答案:

答案 0 :(得分:8)

我会避免以下

   sql.append("SELECT * FROM ").append("dogs_table");
   sql.append(" WHERE ").append(colName).append("='");
                        sql.append(colValue).append("'");

而是使用PreparedStatement及其关联的参数设置方法(setString())等。这将防止colValue的值出现问题和SQL注入攻击(或更普遍) ,colValue形成一些SQL语法)。

如果集合只是空的话,我会从不返回null。这看起来非常违反直觉,而且从客户的角度来看完全出乎意料。

我不建议在错误条件下返回null,因为您的客户端必须明确检查这个(并且可能会忘记)。如果需要,我会返回一个空集合(这可能类似于你的注释re。一个空对象),或者更可能抛出异常(取决于环境和严重性)。该例外是有用的,因为它将携带与遇到的错误有关的一些信息。 Null告诉你什么。

如果在构建Dog对象时遇到问题,您应该怎么做?我认为这取决于您希望应用程序的强大和弹性。返回Dog的子集是一个问题,还是完全是灾难性的,你需要报告这个?这是一个应用程序要求(我必须满足过去的任何一种情况 - 尽力而为全有或全无)。

一些观察结果。我使用的是HashMap而不是旧Hashtable(已同步进行所有访问,更重要的是,不是正确的Collection - 如果您有Collection,则可以通过出于类似原因,期望任何 Collection)和StringBuilder超过StringBuffer的任何其他方法。这不是一个重大问题,但值得了解。

答案 1 :(得分:5)

你问五个问题

  

1。你有什么方法可以改善这种归还狗的技术,具有一些属性?

实际上有几个。

  • 你的方法是静态的 - 这并不可怕,但会导致你使用另一个静态的“executeQuery”,这对我来说有点像辛格尔顿......
  • “Dogs”这个类违反了OO命名惯例 - 复数名词不能成为好的类名,除非该类的一个实例拥有一组东西 - 而且看起来Dogs实际上是“Dog”。
  • HashTable 几乎全部弃用。 HashMap或ConcurrentHashMap提供更好的性能。
  • 我看不出有理由用多个追加创建查询的第一部分 - 它不错,但它的可读性低于它可能的,所以sql.append(“SELECT * FROM dogs_table WHERE”);如果您只是要对所选列(*)和表名(dogs_table)进行硬编码,那么这将是一个更明智的开端。
  

2。 rs.next()将为null ResultSet返回false,或者将生成异常

这似乎不是一个问题,但是,只要不再有任何要处理的行,rs.next()就会返回false。

  

3。如果在从ResultSet的当前行初始化dog对象时发生了一些不好的事情

,该怎么办?

如果“发生了不好的事情”,你接下来要做的就取决于你和你的设计。有宽容的方法(返回你可以的所有行)和不宽容(抛出异常)。我倾向于倾向于“无情”的方法,因为使用“宽容”的方法,用户不会知道你没有返回所有存在的行 - 只是你在错误之前得到的所有行。但是可能存在宽容方法的情况。

  

4。我想你们都会同意这一点:没有什么不好的事情发生,审讯中没有行,会返回空值。

这不是一个明显的正确的答案。首先,它不是所写方法中发生的事情。它将返回一个空的HashTable(这是“null对象”的意思)。其次,在“未找到结果”的情况下,null并不总是答案。

我见过null,但我也看到了一个空的结果变量。我声称它们都是正确的方法,但我更喜欢空的结果变量。但是,最好保持一致,所以选择一种返回“无结果”并坚持下去的方法。

  

5。我注意到人们和一群人说应该将应用程序分成几层或某种层次。

这比其他人更难回答,而没有看到你申请的其余部分。

答案 2 :(得分:4)

Null Object Pattern是一种设计模式,您始终返回一个对象以避免NPE:s和代码中的任何空检查。在您的情况下,这意味着不是返回null,而是返回空Hashtable<Long, Dogs>

原因是因为它是一个集合而你的其他代码会这样访问它,所以如果你返回一个空集合它就不会分解;它不会被迭代,它不会包含任何令人惊讶的东西,它不会导致NPE:s被抛出等等。

确切地说,Null对象是类/接口的特殊实现,它绝对没有任何作用,因此没有任何类型的副作用。由于它不是null使用它的性质会使你的代码更清晰,因为当你知道你总是会从你的方法调用获得一个对象,无论方法内部发生了什么你甚至不必检查空值,也不要让代码对它们作出反应!因为Null Object没有做任何事情,你甚至可以将它们放在singletons只是躺在那里,从而通过这样做来节省内存。

答案 3 :(得分:3)

不要像连接字符串一样构建SQL查询:

sql = new StringBuffer();
sql.append("SELECT * FROM ").append("dogs_table");
sql.append(" WHERE ").append(colName).append("='");
sql.append(colValue).append("'");

这使您的代码容易受到众所周知的安全攻击SQL injection的攻击。不要这样做,而是使用PreparedStatement并通过调用相应的set...()方法来设置参数。注意,您只能使用它来设置列,您无法使用它来动态构建列名称,就像您正在做的那样。例如:

PreparedStatement ps = connection.prepareStatement("SELECT * FROM dogs_table WHERE MYCOL=?");
ps.setString(1, colValue);

rs = ps.executeQuery();

如果使用PreparedStatement,JDBC驱动程序将自动处理转义colValue中可能存在的某些字符,以便SQL注入攻击不再起作用。

答案 4 :(得分:2)

如果发生错误,请抛出异常。如果没有数据,则返回空集合,而不是null。 (另外,通常你应该返回更通用的'Map',而不是具体的实现),

答案 5 :(得分:0)

通过使用Spring-JDBC而不是普通的JDBC,可以显着减少样板JDBC代码的数量。这是使用Spring-JDBC重写的相同方法

public static Hashtable<Long, Dogs> getSomeDogs(String colName, String colValue) {

    StringBuffer sql = new StringBuffer();
    sql.append("SELECT * FROM ").append("dogs_table");
    sql.append(" WHERE ").append(colName).append("='");
    sql.append(colValue).append("'");

    Hashtable<Long, Dogs> result = new Hashtable<Long, Dogs>();

    RowMapper mapper = new RowMapper() {

        public Object mapRow(ResultSet rs, int rowNum) throws SQLException {
            Dogs dog = new Dogs();
            //...initialize the dog from the current resultSet row
            result.put(new Long(dog.getId()), dog);
        }
    };
    (Hashtable<Long, Dogs>) jdbcTemplate.queryForObject(sql, mapper);
}

Spring负责:

  1. 迭代ResultSet
  2. 关闭ResultSet
  3. 始终如一地处理异常
  4. 正如其他人所说,你真的应该使用PreparedStatement来构造SQL而不是String(或StringBuffer)。如果由于某种原因你无法做到这一点,你可以通过构建这样的SQL来提高查询的可读性:

        String sql = 
            "SELECT * FROM dogs_table " +
            "WHERE " + "colName" + " = '" + colValue + "'";