2

我有一个看起来像这样的方法:

try {
  doStuff();
} catch (Exception ex) {
  logger.error(ex);
}

(我并没有真正使用像 doStuff 这样的方法名称——这只是为了让事情变得简单)

在 doStuff 中,我做了很多事情,其中​​包括调用以以下内容结尾的数据访问方法(因此,doStuff 中的另一个方法):

} catch (SQLException ex) {
  logger.error(ex);
} finally {
  try {
    connection.close();
    proc.close();
    results.close();
  } catch (Exception e) {
    logger.error(e);
  } //<--Exception thrown here. HUH?
}
return stuff;

单步执行此代码时,我到达倒数第二个大括号(标有注释),然后跳到第一个代码块中的 catch 处,并出现 NullPointer 异常。这results.close()是它之前正在运行的内容(结果不为空)。我的 IDE (NetBeans) 不提供堆栈跟踪(它显示堆栈跟踪为空)或除异常名称之外的任何其他信息(据我所知)。

此代码之前运行良好。事实上,当它运行时,我更改了数据访问方法(我得到这个异常的地方)正在调用的存储过程,然后这个错误开始发生(应用程序根本没有停止)。从那以后,我尝试重建和重新启动,但无济于事。我可以将 sproc 改回来,但我真的很想知道这个错误是从哪里来的,因为考虑到异常发生在代码的哪个位置,sproc 甚至会成为其中的一部分是没有意义的。

4

6 回答 6

3

以与获取资源相反的顺序关闭资源:

try 
{ 
    results.close(); 
    proc.close(); 
    connection.close(); 
} 
catch (Exception e) 
{ 
    logger.error(e); 
} //<--Exception thrown here. HUH? 

我也推荐这样的方法:

public class DatabaseUtils
{
    // similar for ResultSet and Statement
    public static void close(Connection c)
    {
       try
       {
           if (c != null)
           {
               c.close();
           }
       }
       catch (SQLException e)
       {
           // log or print.
       }
    }
}
于 2010-02-18T18:45:51.507 回答
3

你的 doStuff() 方法抛出了 SQLException 以外的东西,并且没有被捕获。添加一个 catch(Exception e) 块并记录该异常,看看会发生什么。

此代码示例表现出您所描述的相同行为:

public class TryCatchTest {
    public static void main(String[] args) {
        try {
            System.out.println("foo");
            throw new NullPointerException();
        } finally {
            try {
                System.out.println("bar");
            } catch (Exception e) {
                e.printStackTrace();
            }
        } // exception thrown here
    }
}
于 2010-02-18T18:53:56.607 回答
2

很可能 logger 为空。

难以确定的异常通常会在异常处理程序本身中引发。

于 2010-02-18T18:39:40.330 回答
2

NullPointerException 不能在没有语句的情况下在一行中抛出。

检查您正在执行的类文件是否与您查看的源版本相同(当错误配置的类路径包含一个类两次并且在类路径中首先找到旧版本,或者重新编译的类文件时,我遇到了类似的问题未正确复制到我用于测试的 Web 容器中)。

编辑:正如 emh 所指出的,也可能是在进入 finally 块之前发生了异常。

于 2010-02-18T18:46:28.597 回答
0

我 99% 确定这发生在 JDBC 驱动程序中。首先,您的关闭语句是倒退的。您应该按该顺序关闭结果集、语句和连接。

如果您在管理事务的应用程序服务器中运行,则尝试提交事务可能会触发 JDBC 驱动程序内部的异常。

它也可能是关于如何在存储过程中生成结果集,例如访问一个,然后访问另一个,然后返回第一个。

于 2010-02-18T18:54:06.177 回答
0

正如我在评论中所说,永远不要捕获您不想处理的异常。在您的代码中,假设它是完整的,那么您没有对异常做任何有趣的事情,这会导致您对异常发生的位置和原因感到困惑。如果您真的想做的不仅仅是 log or printStackTrace(),例如用特定于域的异常(例如 your.package.DataAccessException 或其他东西)包装它,那就太好了。

做更多这样的事情:


ResultSet results = null;
CallableStatement proc = null;
Connection connection = null;
try {
  connection = >
  proc = connection.createCallableStatement(..);
  results = proc.execute(..);
  >
} finally {
  try {
    if ( results != null ) {
      results.close();
    }
  } catch (Exception e) {
    logger.error(e);
  }
  try {
    if ( proc != null ) {
      proc.close();
    }
  } catch (Exception e) {
    logger.error(e);
  }
  try {
    if ( connection != null ) {
      connection.close();
    }
  } catch (Exception e) {
    logger.error(e);
  }
}

然后在调用者中:


    try {
        doStuff();
    } catch ( SQLException e ) {
        throw new your.package.DataAccessException(e);
        // or just let the SQLException propagate upward
    } catch ( Exception e ) {
        throw new your.package.AppException("omg, crazy pills!", e);
        // or, once again, just let the exception
        //  propagate and don't catch anything
    }

所以,外卖:

  1. 不要在它们发生的地方记录异常,只需传递它们,嵌套在另一个异常中。您不希望您的进程不知道 SQL 操作是否成功。你宁愿停下来做点别的事。
  2. 嵌套异常直到到达行尾,这样,您始终在要处理异常的地方拥有完整的跟踪,而不是分散在整个服务器日志中的五个地方。
  3. 嵌套异常(是的,我说过两次!)这样您就不必关心 JVM 实际从哪里抛出异常,您有下一个异常要遵循,告诉您它实际上是一个可调用语句,或者不正确地关闭您的资源, ETC。
  4. 不要嵌套并从finally代码中捕获的错误中抛出异常,这会干扰原始异常,并且比关闭失败和一开始没有打开的语句更有趣。
  5. 在使用它们之前将变量设置为null,然后在close()ing 之前检查它们是否为 null。
  6. 单独捕获finally块中的每个问题,因为您可能无法关闭您的ResultSet(因为某些执行错误导致它首先无法打开),但您仍然应该尝试关闭CallableStatementand Connection,因为它可能不受影响并且会仍然导致您泄漏资源。

希望有帮助。

于 2010-02-18T19:23:58.340 回答