在简单的数据库驱动Java web应用程序中使用异常

在简单的数据库驱动Java web应用程序中使用异常,java,exception,web-applications,exception-handling,dao,Java,Exception,Web Applications,Exception Handling,Dao,我有一个关于中型Java web应用程序中异常的问题。 有一个使用JDBC实现的数据访问层,其逻辑主要集中在servlet中(UI是JSP)。这样的应用程序的常规异常层次结构是什么 我应该捕获数据访问层上的异常并重新显示整个异常(例如DataAccessException),还是简单地让最高级别处理它们(servlet) 此外,我有一个在数据访问层中调用的连接池,它有自己的异常类型。这些异常应该在数据访问层内捕获并作为DataAccessException重新发布,还是应该由更高级别直接处理 有

我有一个关于中型Java web应用程序中异常的问题。 有一个使用JDBC实现的数据访问层,其逻辑主要集中在servlet中(UI是JSP)。这样的应用程序的常规异常层次结构是什么

我应该捕获数据访问层上的异常并重新显示整个异常(例如DataAccessException),还是简单地让最高级别处理它们(servlet)

此外,我有一个在数据访问层中调用的连接池,它有自己的异常类型。这些异常应该在数据访问层内捕获并作为DataAccessException重新发布,还是应该由更高级别直接处理

有一个主应用程序异常和两个子项:LogicException和TechnicalException,这是一个好主意。逻辑将具有类似于AuthentificationFailedException等的子类,而TechnicalException将负责传递有关失败的信息,如数据访问层异常、FileNotFound(尽管它应该是)等


谢谢大家!

在应用程序中,我看到所有异常都在抛出它们的层中捕获,包装在一个更通用的异常(在您的示例中是DataAccessException)中,然后以这种方式重新抛出。 这是因为通常引发异常的层没有足够的上下文信息,因此无法决定如何正确处理错误。处理这些异常的最佳位置是位于抛出异常的层正上方的层:它有足够的信息“优雅地失败”,而不会让异常在堆栈上走得太远。 在您的情况下,在servlet中捕获异常就可以完成这项工作


但是异常的层次结构取决于您正在使用的应用程序。您的逻辑技术部门可能是一个不错的选择。。。或者可能不会在“异常处理”中没有“正确”的选择,而且这是一个太复杂的参数,无法在单个问答对中处理。:)

我还参与开发一个中型web应用程序。我们使用的方法是将异常从数据库层抛出到servlet,servlet处理捕获异常并向用户生成正确的错误消息的逻辑

这是不是我要走的路。 原因:整个servlet现在依赖于实现。假设我们想要切换实现(目前我们想要切换)。我们现在需要更改servet其余部分中的每个捕获,因为新的实现不会抛出相同的异常。一般例外是正确的选择


不仅例外,业务对象也应该是泛型的。最好是接口。这将允许您更轻松地更改实现,而您的locig处理代码将在servlet的整个生命周期中依赖于相同的对象。

通常,我会将较低级别的异常与较高级别、更有意义的异常包装在一起。这通常需要更多的工作,但可以在层之间实现解耦

假设我正在编写一个配置子系统,碰巧从数据库中读取数据。如果我不包装,我会有这样的东西:

public String getConfigurationProperty(String name) throws SQLException {
    // Try to read from my configuration table
}
如果我做包装,我会

public String getConfigurationProperty(String name) throws ConfigurationException {
    try {
        // Try to read from my configuration table 
    } catch (SQLException ex) {
        ConfigurationException wrapper = // Some subclass of ConfigurationException that wraps ex
        throw wrapper;
    }
}
这肯定是更多的工作。这样做的好处是,如果以后我想将配置后端更改为(比如)基于文件的后端,而不使用包装器,那么我的方法将变得非常简单

public String getConfigurationProperty(String name) throws IOException {
    // Try to read from my configuration file
}
然后我必须更改所有客户端代码以处理
IOException
s,而不是
SQLException
s。如果您进行包装,您只需要更改后端,因为您的客户机代码已经在编写时考虑了
ConfigurationException
s及其子类。请注意,这与是否使用已检查或未检查的异常无关:如果要进行异常处理,几乎总是需要知道要处理的异常类型的近似值

现在,我倾向于这样做。有人认为大多数异常无论如何都无法正确处理,所有这些包装在大多数情况下都是胡说八道

public String getConfigurationProperty(String name) throws ConfigurationException {
    try {
        // Try to read from my configuration file 
    } catch (IOException ex) {
        ConfigurationException wrapper = // Some subclass of ConfigurationException that wraps ex
        throw wrapper;
    }
}

通常,在应用程序的任何给定层中,您都希望捕获的异常对该层有意义。因此,您不希望在Servlet(或MVC控制器)中捕获SybaseException,因为您可能有一个DAO层来隐藏该级别的实现细节

两个一般提示:

  • 您应该将apache+servlet容器配置为返回 合理的页面上有500个错误
  • 控制器应使用用户友好的 消息捕获的异常应该为 控制员应制定合理的信息
  • 我将考虑子类RunTimeExtor与基本应用程序。 异常,它既包含技术错误消息,也包含 本地化用户错误消息
  • 当你发现异常时,想想你想对它做什么。设计您的异常,以便可以直接执行您需要的操作

最后,并不是每个人都同意这一点,但您可以提出一个强有力的理由,使您的所有异常子类RuntimeException,这样它们就可以被取消选中。我已经研究过许多采用这种方法的大型代码库,它通常工作得很好,并且减少了很多catch-rethrow样板代码。仅仅因为java提供了检查过的异常,并不意味着您必须使用它们。这显然是我的观点,但重要的是要认识到,并不是每个人都使用检查异常,事实上,许多代码库会明确避免它们。

这类问题有标签吗?关于如何大规模构造代码的问题?我觉得这门课很有趣,非常需要一本好的书