异常和 SQLException 的区别

Posted

技术标签:

【中文标题】异常和 SQLException 的区别【英文标题】:Difference between Exception and SQLException 【发布时间】:2014-05-21 14:49:56 【问题描述】:

有人可以解释捕获Exception 和捕获SQLException 之间的区别吗?我知道如果您选择打印异常错误,SQLException 会打印出更多信息,但还有什么其他的吗?

try 
   //code
 catch(Exception ex) 
   //code

try 
   //code
 catch(SQLException ex) 
   //code

在catch块中使用ExceptionSQLException有什么好处和区别?

【问题讨论】:

Exception 是一种更通用的类型,是 SQLException 的父类。它用于与 SQL 和数据库相关的上下文中,而不是一般上下文中。使用与正在发生的活动相关的异常很重要...这意味着使用好的代码您可能更有可能看到 SQL 类型的 SQL 异常... "我知道如果你选择打印异常错误,SQLException 会打印出更多信息" 不。不管你是在 Exception 类型的变量中捕获它还是在 SQLException 类型的变量中捕获它,它仍然是同一个 SQLException 对象,并且将包含相同的信息。 【参考方案1】:

Exception 是一个标准类,每个异常都从该类继承。

SQLException 是一个继承自 Exception 的类,专为数据库 (SQL) 异常而设计。

通过做

try 
    // Your code here
 catch (Exception e) 
    // Catching here

您正在捕获所有可能的异常类型...但是,您可能无法知道如何对特定异常做出反应。

但是通过做

try 
    // Your code here
 catch (SQLException e) 
    // Catching here

您知道在处理数据库时发生了异常,它可以帮助您了解如何对异常做出反应。

【讨论】:

【参考方案2】:

SQLException 继承自 Exception,因此 SQLException 将包含比 Exception 更多(和更具体)的信息(旨在普遍适用于所有异常)。

您还可以有多个catch 子句;所以你可以先尝试捕捉SQLException,但如果不是SQLException,那么你可以捕捉一般的Exception

一般来说,您不应该catch 异常,除非您打算以某种方式处理它们。您可以有一个***异常处理程序来捕获任何冒泡到调用堆栈顶部的异常,这样您的程序就不会因未处理的异常而崩溃。

【讨论】:

【参考方案3】:

如您所见,SQLException 扩展异常。所以这是唯一的区别。当您捕获异常时,您将捕获所有异常(这很糟糕)。但是当你捕捉到 SQLException 时,你只会捕捉到那个(这很好,因为那是你正在寻找的)。

【讨论】:

【参考方案4】:

这不是唯一的区别。

捕获Exception 是危险的,因为它还捕获所有RuntimeExceptions(因此是未经检查的异常),其中包括NullPointerException 等明显的程序员错误。 不要那样做

另外,Exception 与其他类一样,因此您可以将其子类化并添加您的构造函数/方法。例如,SQLException 有一个 .getErrorCode() method,而 Exception 没有。如果只捕获Exception,则无法访问此方法。

一般来说,首先捕获“更精确”的异常是最好的。例如,使用新的(在 Java 7...中)文件 API,您可以轻松区分文件系统级错误和其他 I/O 错误,因为 FileSystemException 扩展了 IOException

try 
    something();
 catch (FileSystemException e) 
    // fs level error
 catch (IOException e) 
    // I/O error

【讨论】:

所以 SQLException 将是一个实例,您正在捕获更具体而不是通用的东西? 是的,完全正确。而且总是最好先捕获更具体的异常!如果你抓住“太低”,你可能会抓住不受欢迎的人。 完全有道理,非常感谢您的帮助! 还要注意,如果你实现/覆盖一个声明为throws A的方法,你可以选择让你的实现抛出A或任何子类。【参考方案5】:

如果在 try 和 catch 块之间抛出一个不是 SQL 异常的异常(这些通常仅来自与数据库相关的代码),例如空指针异常,异常捕获将捕获它,但 SQLException不会。

【讨论】:

【参考方案6】:

SQLException 是一个异常,所以你只是得到一个更具体的异常。

根据Oracle的javadocs,你得到的具体信息是:

描述错误的字符串。这用作 Java 异常 消息,可通过 getMessage 方法获得。 一个“SQLstate”字符串,它跟在 XOPEN SQLstate 之后 约定或 SQL:2003 约定。 SQLState 的值 字符串在相应的规范中进行了描述。数据库元数据 方法 getSQLStateType 可用于发现驱动程序是否 返回 XOPEN 类型或 SQL:2003 类型。 特定于每个供应商的整数错误代码。通常这个 将是底层数据库返回的实际错误代码。 到下一个异常的链。这可以用来提供额外的 错误信息。 此 SQLException 的因果关系(如果有)。

【讨论】:

【参考方案7】:

SQLException 是从Exception 派生的特殊异常。

如果你捕捉到Exception,所有的异常都会被捕捉到。甚至是不受欢迎的例外。

如果你只捕捉到它的特化,SQLException,只有 SQLException 本身或其派生会被捕捉到。

一个人应该只捕获一个可以处理或希望处理的异常,并让其他人冒泡。

如需进一步参考,请查看以下内容:

    Exception SQLException

【讨论】:

【参考方案8】:

当您谈论捕获异常时,这都是关于层次结构的。

从技术上讲,Exception - 是捕获每个异常的超类。 如果您在 try 块中编写与 SQL 相关的内容,并且您知道它甚至可能抛出 SQL 异常。 那么你也可以这样声明。

        try
        

        catch(SQLException ex)
        
        Do this,when SQL Exception is caught.
        
        catch(Exception ex)
        
        Generic Exception - works for all
        

【讨论】:

【参考方案9】:

SQL 异常是在 Java 数据库连接 (JDBC) 中工作时经常出现的错误。它与使用准备好的语句访问或设置 SQL 查询中的列有关。

SQLException 是从 Exception 派生的,包含更具体的与访问或设置 SQL 查询中的列相关的信息,而异常通常更笼统。

【讨论】:

【参考方案10】:

A - 解释

SQLExceptionjava.lang.Exception 的子类型,它也实现了Iterable<Throwable> 类。程序员更喜欢抛出 Exception 类的不同子类型,因为在更高级别上,他们希望捕获确切的子异常类,以便他们可以确保在某些确切情况下抛出特定的异常。因此,他们可以知道异常的确切来源。

B - 示例

假设您编写了一个引发多个异常的方法。假设您获取一个 json 字符串并对其进行解析,然后将其保存在数据库中。考虑以下方法;

public boolean persistCustomer(String jsonString) throws SQLException, IOException 
    Connection conn = getConnection(); 
    PreparedStatement preparedStatement = null;


    ObjectMapper objectMapper = new ObjectMapper();
    try 
        Customer customer = objectMapper.readValue(jsonString, Customer.class);

        preparedStatement = conn.prepareStatement(PERSIST_CUSTOMER);

        preparedStatement.setString     (1,  customer.getName());
        preparedStatement.setInt        (2,  customer.getAge());
        preparedStatement.setBoolean    (3,  customer.getIsRegular());

        preparedStatement.executeUpdate();

        return true;
     catch (IOException e) 
        throw e;
     finally 
        try 
            if (preparedStatement != null)
                preparedStatement.close();
            if (conn != null)
                conn.close();
         catch (SQLException e) 
            e.printStackTrace();
        
    

在此方法中,我们将 JSON 转换为 Customer 类,并将客户类持久化到数据库中。

以下行抛出SQLException;

    preparedStatement = conn.prepareStatement(PERSIST_CUSTOMER);

    preparedStatement.setString     (1,  customer.getName());
    preparedStatement.setInt        (2,  customer.getAge());
    preparedStatement.setBoolean    (3,  customer.getIsRegular());

    preparedStatement.executeUpdate();

prepareStatement()、setter 和 executeUpdate() 方法,它们都在抛出 SQLException 的。而且,我们将 String 中的 JSON 转换为 Customer 对象的那一行,除了 SQLException 之外,也抛出了几个异常。

Customer customer = objectMapper.readValue(jsonString, Customer.class);

readValue() 方法抛出 JsonParseExceptionJsonMappingExceptionIOException。所有这些都可以使用IOException 捕获,因为与 JSON 相关的异常扩展了IOException

我将提供两个不同的示例,以便清楚地理解为什么我们需要不同类型的异常。

C - 坏习惯:使用异常捕获所有异常

public class BadPracticeExample 

    public static void main(String[] args) 
        mysqlUtil dbUtil =  new MySQLUtil();

        String jsonString = "\"name\":\"Levent\",\"age\":31,\"isRegular\":true";

        try 
            dbUtil.persistCustomer(jsonString);
         catch (Exception e) 
            System.out.println("A problem occured");
        
    


如您所见,它捕获了异常,但如果我们需要针对两种不同的问题来源进行特殊异常处理,我们该怎么办? persistCustomer 可以抛出 IOException 或 SQLException ,如果我们需要执行不同的任务集来处理这些问题怎么办?我想在发生 SQLException 时向数据库管理员发送电子邮件,并且我想在发生 JSON 解析问题时继续,在捕获 IOException 的情况下?

在这种情况下,您不能这样做。这是上面代码 sn-p 的输出,我们只确定发生了异常,但我们不知道它的来源;

A problem occured

D - 良好实践示例 I:捕获的 SQL 异常

public class GoodPracticeExample 

    public static void main(String[] args) 
        MySQLUtil dbUtil =  new MySQLUtil();

        String jsonString = "\"name\":\"Levent\",\"age\":31,\"isRegular\":true";

        try 
            dbUtil.persistCustomer(jsonString);
         catch (SQLException e) 
            System.out.println("SQL Exception catched, SQL State : " + e.getSQLState());
            System.out.println("Error Code                       : " + e.getErrorCode());
            System.out.println("Error Message                    : " + e.getMessage());
         catch (IOException e) 
            System.out.println("Cannot parse JSON : " + jsonString);
            System.out.println("Error Message     : " + e.getMessage());
        
    


如您所见,我们同时捕获了 JSON 和 SQL 问题,在此示例中,子方法试图在没有表的情况下持久化 DB。输出如下;

SQL Exception catched, SQL State : 42000
Error Code                       : 1142
Error Message                    : INSERT command denied to user 'levent'@'example.com' for table 'CUSTOMER'

所以我们已经捕获了 SQL 异常,并且我们拥有发送警报电子邮件所需的所有参数。我们可以在 SQLException catch 块上添加额外的处理程序或实用程序方法。

D - 良好实践示例 II:IOExceptoin 遇到解析错误

public class GoodPracticeExample 

    public static void main(String[] args) 
        MySQLUtil dbUtil =  new MySQLUtil();

        String jsonString = "\"Zname\":\"Levent\",\"age\":31,\"isRegular\":true";

        try 
            dbUtil.persistCustomer(jsonString);
         catch (SQLException e) 
            System.out.println("SQL Exception catched, SQL State : " + e.getSQLState());
            System.out.println("Error Code                       : " + e.getErrorCode());
            System.out.println("Error Message                    : " + e.getMessage());
         catch (IOException e) 
            System.out.println("Cannot parse JSON : " + jsonString);
            System.out.println("Error Message     : " + e.getMessage());
        
    


如果你注意到了,我已经破坏了 JSON 以导致 IOException。现在在 json 字符串中,写入了“Zname”而不是“name”,这将导致 Jackson Parser 失败。让我们检查一下输出这段代码。

Cannot parse JSON : "Zname":"Levent","age":31,"isRegular":true
Error Message     : Unrecognized field "Zname" (class com.divilioglu.db.utils$Customer), not marked as ignorable (3 known properties: "isRegular", "name", "age"])
 at [Source: (String)""Zname":"Levent","age":31,"isRegular":true"; line: 1, column: 11] (through reference chain: com.divilioglu.db.utils.MySQLUtil$Customer["Zname"])

如您所见,我们捕捉到了具体场景,我们确信,这来自 dbUtil.persistCustomer() 方法中的行,如下所示;

Customer customer = objectMapper.readValue(jsonString, Customer.class);

E - 结论

因此,通过扩展现有的异常类来创建新的异常是一种最佳实践。在开始编写代码时,您可能会认为这是一种矫枉过正,您不需要额外的异常类,但是当您需要区分问题的根源并独立处理它们时,您将需要它们。

在上面演示的这个示例中,我可以独立捕获IOExceptionSQLException,并且两个异常的来源都来自同一个方法。我想区分两者,以便我可以独立处理它们。如果你只是用基类 Exception 类包装所有的 Exceptions,你就无法拥有这种灵活性。

【讨论】:

以上是关于异常和 SQLException 的区别的主要内容,如果未能解决你的问题,请参考以下文章

ResultSet 关闭后不允许出现 ResultSet、空指针异常和 SQLException 操作

System.Data.Entity.Core.EntityCommandExecutionException 和内部异常 SqlException:无效的列名“xxxx”

java.sql.SQLException:Io 异常:指定了未知主机

Android Studio 未报告异常 SQLException & ClassNotFoundException

java.sql.SQLException: Io 异常: NL 异常产生

java.sql.SQLException:Io 异常:套接字读取超时与关闭连接