Java 中文官方教程 2022 版(三十四)(3)

简介: Java 中文官方教程 2022 版(三十四)

Java 中文官方教程 2022 版(三十四)(2)https://developer.aliyun.com/article/1488028

使用 DataSource 对象进行连接

原文:docs.oracle.com/javase/tutorial/jdbc/basics/sqldatasources.html

本节涵盖了DataSource对象,这是获取与数据源连接的首选方法。除了其他优点,稍后将解释的优点之外,DataSource对象还可以提供连接池和分布式事务。这种功能对企业数据库计算至关重要。特别是,它对企业 JavaBeans(EJB)技术至关重要。

本节向您展示如何使用DataSource接口获取连接以及如何使用分布式事务和连接池。这两者在您的 JDBC 应用程序中都涉及非常少的代码更改。

部署执行这些操作的类所需的工作,通常由系统管理员使用工具(如 Apache Tomcat 或 Oracle WebLogic Server)完成,取决于部署的DataSource对象的类型。因此,本节大部分内容都致力于展示系统管理员如何设置环境,以便程序员可以使用DataSource对象获取连接。

下面的主题包括:

  • 使用 DataSource 对象获取连接
  • 部署基本的 DataSource 对象
  • 部署其他 DataSource 实现
  • 获取和使用池化连接
  • 部署分布式事务
  • 使用连接进行分布式事务

使用 DataSource 对象获取连接

在建立连接中,您学习了如何使用DriverManager类获取连接。本节将向您展示如何使用DataSource对象获取与数据源的连接,这是首选方法。

由实现DataSource类的类实例化的对象代表特定的 DBMS 或其他数据源,比如文件。DataSource对象代表特定的 DBMS 或其他数据源,比如文件。如果一个公司使用多个数据源,它将为每个数据源部署一个单独的DataSource对象。DataSource接口由驱动程序供应商实现。它可以以三种不同的方式实现:

  • 基本的DataSource实现会生成标准的未池化或未用于分布式事务的Connection对象。
  • 支持连接池的DataSource实现会生成参与连接池的Connection对象,即可以被回收的连接。
  • 支持分布式事务的DataSource实现会生成可用于分布式事务的Connection对象,即访问两个或多个 DBMS 服务器的事务。

JDBC 驱动程序应至少包含基本的DataSource实现。例如,Java DB JDBC 驱动程序包括org.apache.derby.jdbc.ClientDataSource的实现,而对于 MySQL,则是com.mysql.jdbc.jdbc2.optional.MysqlDataSource。如果您的客户端在 Java 8 紧凑配置文件 2 上运行,则 Java DB JDBC 驱动程序是org.apache.derby.jdbc.BasicClientDataSource40。本教程的示例需要紧凑配置文件 3 或更高版本。

支持分布式事务的DataSource类通常也实现了对连接池的支持。例如,由 EJB 供应商提供的DataSource类几乎总是支持连接池和分布式事务。

假设前面示例中茁壮成长的 The Coffee Break 连锁店的所有者已决定通过互联网进一步扩展销售咖啡。随着预期的大量在线业务,所有者肯定需要连接池。打开和关闭连接涉及大量开销,所有者预计这个在线订购系统将需要大量的查询和更新。通过连接池,一组连接可以一遍又一遍地使用,避免为每次数据库访问创建新连接的开销。此外,所有者现在有第二个包含最近收购的咖啡烘焙公司数据的 DBMS。这意味着所有者希望能够编写使用旧 DBMS 服务器和新 DBMS 服务器的分布式事务。

链店所有者已重新配置计算机系统以服务新的、更大的客户群。所有者已购买最新的 JDBC 驱动程序和与之配套的 EJB 应用服务器,以便使用分布式事务并获得连接池带来的性能提升。许多与最近购买的 EJB 服务器兼容的 JDBC 驱动程序可用。现在,所有者拥有三层架构,中间层有一个新的 EJB 应用服务器和 JDBC 驱动程序,第三层是两个 DBMS 服务器。发出请求的客户端计算机是第一层。

部署基本的 DataSource 对象

系统管理员需要部署DataSource对象,以便 The Coffee Break 的编程团队可以开始使用它们。部署DataSource对象包括三个任务:

  1. 创建DataSource类的实例
  2. 设置其属性
  3. 使用使用 Java 命名和目录接口(JNDI)API 的命名服务进行注册

首先,考虑最基本的情况,即使用DataSource接口的基本实现,即不支持连接池或分布式事务的实现。在这种情况下,只需要部署一个DataSource对象。DataSource的基本实现产生与DriverManager类产生的相同类型的连接。

创建 DataSource 类的实例并设置其属性

假设一家只想要一个基本的DataSource实现的公司从 JDBC 供应商 DB Access, Inc 购买了一个驱动程序。该驱动程序包括实现DataSource接口的类com.dbaccess.BasicDataSource。以下代码摘录创建BasicDataSource类的实例并设置其属性。部署BasicDataSource实例后,程序员可以调用DataSource.getConnection方法获取连接到公司数据库CUSTOMER_ACCOUNTS。首先,系统管理员使用默认构造函数创建BasicDataSource对象*ds。然后,系统管理员设置三个属性。请注意,以下代码通常由部署工具执行:

com.dbaccess.BasicDataSource ds = new com.dbaccess.BasicDataSource();
ds.setServerName("grinder");
ds.setDatabaseName("CUSTOMER_ACCOUNTS");
ds.setDescription("Customer accounts database for billing");

变量*ds*现在代表安装在服务器上的数据库CUSTOMER_ACCOUNTS*ds*对象生成的任何连接都将是到数据库CUSTOMER_ACCOUNTS的连接。

使用使用 JNDI API 的命名服务注册 DataSource 对象

设置属性后,系统管理员可以将BasicDataSource对象注册到 JNDI(Java 命名和目录接口)命名服务中。通常使用的特定命名服务通常由系统属性确定,这里没有显示。以下代码摘录注册BasicDataSource对象并将其绑定到逻辑名称jdbc/billingDB

Context ctx = new InitialContext();
ctx.bind("jdbc/billingDB", ds);

此代码使用 JNDI API。第一行创建一个InitialContext对象,它类似于文件系统中的根目录的起始点。第二行将BasicDataSource对象*ds*与逻辑名称jdbc/billingDB关联或绑定。在下一个代码摘录中,您将向命名服务提供此逻辑名称,它将返回BasicDataSource对象。逻辑名称可以是任何字符串。在这种情况下,公司决定使用名称billingDB作为CUSTOMER_ACCOUNTS数据库的逻辑名称。

在前面的示例中,jdbc是初始上下文下的一个子上下文,就像根目录下的目录是子目录一样。名称jdbc/billingDB类似于路径名,其中路径中的最后一项类似于文件名。在这种情况下,billingDB是赋予BasicDataSource对象*ds*的逻辑名称。子上下文jdbc保留用于绑定到DataSource对象的逻辑名称,因此jdbc将始终是数据源逻辑名称的第一部分。

使用部署的 DataSource 对象

系统管理员部署了基本的DataSource实现后,程序员就可以开始使用了。这意味着程序员可以提供绑定到DataSource类实例的逻辑数据源名称,JNDI 命名服务将返回该DataSource类的实例。然后可以在该DataSource对象上调用getConnection方法,以获取连接到其表示的数据源的连接。例如,程序员可能会编写以下两行代码来获取一个产生与数据库CUSTOMER_ACCOUNTS连接的DataSource对象。

Context ctx = new InitialContext();
DataSource ds = (DataSource)ctx.lookup("jdbc/billingDB");

第一行代码获取一个初始上下文作为检索DataSource对象的起点。当您向lookup方法提供逻辑名称jdbc/billingDB时,该方法将返回系统管理员在部署时绑定到jdbc/billingDBDataSource对象。因为lookup方法的返回值是一个 Java Object,我们必须将其转换为更具体的DataSource类型,然后将其赋给变量*ds*

变量*ds*是实现DataSource接口的com.dbaccess.BasicDataSource类的实例。调用*ds*.getConnection方法将产生与CUSTOMER_ACCOUNTS数据库的连接。

Connection con = ds.getConnection("fernanda","brewed");

getConnection方法仅需要用户名和密码,因为变量*ds*在其属性中具有与CUSTOMER_ACCOUNTS数据库建立连接所需的其余信息,如数据库名称和位置。

DataSource 对象的优势

由于其属性,DataSource对象是获取连接的比DriverManager类更好的选择。程序员不再需要在其应用程序中硬编码驱动程序名称或 JDBC URL,这使它们更具可移植性。此外,DataSource属性使代码维护更加简单。如果有任何更改,系统管理员可以更新数据源属性,而不必担心更改每个连接到数据源的应用程序。例如,如果数据源被移动到不同的服务器,系统管理员只需将serverName属性设置为新服务器名称。

除了可移植性和易维护性之外,使用DataSource对象获取连接还可以提供其他优势。当DataSource接口被实现以与ConnectionPoolDataSource实现一起工作时,由该DataSource类的实例产生的所有连接将自动成为池化连接。同样,当DataSource实现被实现以与XADataSource类一起工作时,它产生的所有连接将自动成为可用于分布式事务的连接。下一节将展示如何部署这些类型的DataSource实现。

部署其他 DataSource 实现

系统管理员或其他从事该职能的人可以部署一个DataSource对象,以便它产生的连接是连接池连接。为此,他或她首先部署一个ConnectionPoolDataSource对象,然后部署一个实现与其配合工作的DataSource对象。设置ConnectionPoolDataSource对象的属性,以便它表示将生成连接的数据源。在ConnectionPoolDataSource对象已经注册到 JNDI 命名服务后,部署DataSource对象。通常只需设置DataSource对象的两个属性:descriptiondataSourceName。给定给dataSourceName属性的值是先前部署的标识ConnectionPoolDataSource对象的逻辑名称,该对象包含用于建立连接所需的属性。

使用ConnectionPoolDataSourceDataSource对象部署后,您可以在DataSource对象上调用DataSource.getConnection方法并获得一个连接池连接。此连接将连接到ConnectionPoolDataSource对象属性中指定的数据源。

以下示例描述了如何为 The Coffee Break 的系统管理员部署一个实现提供连接池连接的DataSource对象。系统管理员通常会使用部署工具,因此本节中显示的代码片段是部署工具将执行的代码。

为了获得更好的性能,The Coffee Break 公司从 DB Access, Inc.购买了一个 JDBC 驱动程序,其中包括实现ConnectionPoolDataSource接口的com.dbaccess.ConnectionPoolDS类。系统管理员创建此类的实例,设置其属性,并将其注册到 JNDI 命名服务中。The Coffee Break 从其 EJB 服务器供应商 Application Logic, Inc.购买了其DataSourcecom.applogic.PooledDataSourcecom.applogic.PooledDataSource类通过使用ConnectionPoolDataSourcecom.dbaccess.ConnectionPoolDS提供的底层支持来实现连接池。

必须首先部署ConnectionPoolDataSource对象。以下代码创建了com.dbaccess.ConnectionPoolDS的实例并设置其属性:

com.dbaccess.ConnectionPoolDS cpds = new com.dbaccess.ConnectionPoolDS();
cpds.setServerName("creamer");
cpds.setDatabaseName("COFFEEBREAK");
cpds.setPortNumber(9040);
cpds.setDescription("Connection pooling for " + "COFFEEBREAK DBMS");

在部署ConnectionPoolDataSource对象后,系统管理员部署DataSource对象。以下代码将com.dbaccess.ConnectionPoolDS对象*cpds*注册到 JNDI 命名服务中。请注意,与*cpds*变量关联的逻辑名称在jdbc的子上下文下添加了pool,类似于在分层文件系统中的另一个子目录下添加子目录。com.dbaccess.ConnectionPoolDS类的任何实例的逻辑名称始终以jdbc/pool开头。Oracle 建议将所有ConnectionPoolDataSource对象放在jdbc/pool子上下文下:

Context ctx = new InitialContext();
ctx.bind("jdbc/pool/fastCoffeeDB", cpds);

接下来,实现与*cpds*变量和com.dbaccess.ConnectionPoolDS类的其他实例交互的DataSource类被部署。下面的代码创建了这个类的一个实例并设置了其属性。请注意,为com.applogic.PooledDataSource的这个实例只设置了两个属性。设置description属性是因为它总是必需的。另一个设置的属性是dataSourceName,为*cpds*给出了逻辑 JNDI 名称,它是com.dbaccess.ConnectionPoolDS类的一个实例。换句话说,*cpds*代表了将为DataSource对象实现连接池的ConnectionPoolDataSource对象。

下面的代码,可能会被部署工具执行,创建了一个PooledDataSource对象,设置了其属性,并将其绑定到逻辑名称jdbc/fastCoffeeDB

com.applogic.PooledDataSource ds = new com.applogic.PooledDataSource();
ds.setDescription("produces pooled connections to COFFEEBREAK");
ds.setDataSourceName("jdbc/pool/fastCoffeeDB");
Context ctx = new InitialContext();
ctx.bind("jdbc/fastCoffeeDB", ds);

此时,部署了一个DataSource对象,应用程序可以从中获取到数据库COFFEEBREAK的连接池连接。

获取和使用连接池连接

连接池是数据库连接对象的缓存。这些对象代表了可以被应用程序用来连接数据库的物理数据库连接。在运行时,应用程序从连接池请求连接。如果连接池包含可以满足请求的连接,则将连接返回给应用程序。如果找不到连接,则创建一个新连接并返回给应用程序。应用程序使用连接对数据库执行一些工作,然后将对象返回到连接池。连接随后可用于下一个连接请求。

连接池促进了连接对象的重复使用,减少了连接对象被创建的次数。连接池显著提高了数据库密集型应用的性能,因为创建连接对象在时间和资源方面都是昂贵的。

现在这些DataSourceConnectionPoolDataSource对象已经部署,程序员可以使用DataSource对象获取连接池连接。获取连接池连接的代码与获取非连接池连接的代码类似,如下两行所示:

ctx = new InitialContext();
ds = (DataSource)ctx.lookup("jdbc/fastCoffeeDB");

变量*ds*代表一个DataSource对象,用于向数据库COFFEEBREAK生成连接池连接。您只需要检索一次这个DataSource对象,因为您可以使用它生成所需数量的连接池连接。在*ds*变量上调用getConnection方法会自动生成一个连接池连接,因为*ds*变量表示的DataSource对象被配置为生成连接池连接。

连接池对程序员通常是透明的。在使用连接池连接时,只需要做两件事情:

  1. 使用DataSource对象而不是DriverManager类来获取连接。在下面的代码行中,*ds*是一个已实现和部署的DataSource对象,它将创建池化连接,usernamepassword是代表具有访问数据库权限的用户的凭据的变量:
Connection con = ds.getConnection(username, password);
  1. 使用finally语句来关闭池化连接。下面的finally块将出现在应用于使用池化连接的代码的try/catch块之后:
try {
    Connection con = ds.getConnection(username, password);
    // ... code to use the pooled
    // connection con
} catch (Exception ex {
    // ... code to handle exceptions
} finally {
    if (con != null) con.close();
}

否则,使用池化连接的应用程序与使用常规连接的应用程序相同。当进行连接池化时,应用程序员可能注意到的唯一其他事情是性能更好。

以下示例代码获取一个DataSource对象,该对象生成到数据库COFFEEBREAK的连接,并使用它来更新表COFFEES中的价格:

import java.sql.*;
import javax.sql.*;
import javax.ejb.*;
import javax.naming.*;
public class ConnectionPoolingBean implements SessionBean {
    // ...
    public void ejbCreate() throws CreateException {
        ctx = new InitialContext();
        ds = (DataSource)ctx.lookup("jdbc/fastCoffeeDB");
    }
    public void updatePrice(float price, String cofName,
                            String username, String password)
        throws SQLException{
        Connection con;
        PreparedStatement pstmt;
        try {
            con = ds.getConnection(username, password);
            con.setAutoCommit(false);
            pstmt = con.prepareStatement("UPDATE COFFEES " +
                        "SET PRICE = ? " +
                        "WHERE COF_NAME = ?");
            pstmt.setFloat(1, price);
            pstmt.setString(2, cofName);
            pstmt.executeUpdate();
            con.commit();
            pstmt.close();
        } finally {
            if (con != null) con.close();
        }
    }
    private DataSource ds = null;
    private Context ctx = null;
}

此代码示例中的连接参与连接池化,因为以下条件为真:

  • 已部署了一个实现ConnectionPoolDataSource的类的实例。
  • 已部署了一个实现DataSource的类的实例,并为其dataSourceName属性设置的值是之前部署的ConnectionPoolDataSource对象绑定的逻辑名称。

请注意,尽管这段代码与您之前看到的代码非常相似,但在以下方面有所不同:

  • 它导入了javax.sqljavax.ejbjavax.naming包,以及java.sql
    DataSourceConnectionPoolDataSource接口位于javax.sql包中,JNDI 构造函数InitialContext和方法Context.lookup属于javax.naming包。这个特定的示例代码是以一个使用javax.ejb包中的 API 的 EJB 组件的形式呈现的。这个示例的目的是展示您使用池化连接的方式与使用非池化连接的方式相同,因此您不必担心理解 EJB API。
  • 它使用DataSource对象来获取连接,而不是使用DriverManager工具。
  • 它使用一个finally块来确保连接被关闭。

获取和使用池化连接与获取和使用常规连接类似。当某人作为系统管理员部署了一个ConnectionPoolDataSource对象和一个正确的DataSource对象时,应用程序将使用该DataSource对象来获取池化连接。然而,应用程序应该使用一个finally块来关闭池化连接。为简单起见,前面的示例使用了一个finally块但没有catch块。如果try块中的方法抛出异常,它将默认抛出,并且finally子句将在任何情况下执行。

部署分布式事务

可以部署DataSource对象以获取可用于分布式事务的连接。与连接池一样,必须部署两个不同的类实例:一个XADataSource对象和一个实现与其一起工作的DataSource对象。

假设 The Coffee Break 企业家购买的 EJB 服务器包括com.applogic.TransactionalDS类,该类与com.dbaccess.XATransactionalDSXADataSource类一起工作。它可以与任何XADataSource类一起工作,使 EJB 服务器在 JDBC 驱动程序之间具有可移植性。当部署DataSourceXADataSource对象时,生成的连接将能够参与分布式事务。在这种情况下,com.applogic.TransactionalDS类被实现为生成的连接也是池化连接,这通常是作为 EJB 服务器实现的一部分提供的DataSource类的情况。

必须首先部署XADataSource对象。以下代码创建com.dbaccess.XATransactionalDS的一个实例并设置其属性:

com.dbaccess.XATransactionalDS xads = new com.dbaccess.XATransactionalDS();
xads.setServerName("creamer");
xads.setDatabaseName("COFFEEBREAK");
xads.setPortNumber(9040);
xads.setDescription("Distributed transactions for COFFEEBREAK DBMS");

以下代码将com.dbaccess.XATransactionalDS对象*xads*注册到 JNDI 命名服务。请注意,与*xads*关联的逻辑名称在jdbc下添加了子上下文xa。Oracle 建议com.dbaccess.XATransactionalDS类的任何实例的逻辑名称始终以jdbc/xa开头。

Context ctx = new InitialContext();
ctx.bind("jdbc/xa/distCoffeeDB", xads);

部署实现与*xads*和其他XADataSource对象交互的DataSource对象。请注意,DataSourcecom.applogic.TransactionalDS可以与任何 JDBC 驱动程序供应商的XADataSource类一起使用。部署DataSource对象涉及创建com.applogic.TransactionalDS类的实例并设置其属性。dataSourceName属性设置为jdbc/xa/distCoffeeDB,这是与com.dbaccess.XATransactionalDS关联的逻辑名称。这是实现DataSource类的分布式事务能力的XADataSource类。以下代码部署DataSource类的一个实例:

com.applogic.TransactionalDS ds = new com.applogic.TransactionalDS();
ds.setDescription("Produces distributed transaction " +
                  "connections to COFFEEBREAK");
ds.setDataSourceName("jdbc/xa/distCoffeeDB");
Context ctx = new InitialContext();
ctx.bind("jdbc/distCoffeeDB", ds);

现在已经部署了com.applogic.TransactionalDScom.dbaccess.XATransactionalDS类的实例,应用程序可以调用TransactionalDS类的实例上的getConnection方法,以获取连接到COFFEEBREAK数据库的连接,该连接可用于分布式事务。

使用连接进行分布式事务

要获取可用于分布式事务的连接,必须使用已经正确实现和部署的DataSource对象,如部署分布式事务部分所示。使用这样的DataSource对象,调用其上的getConnection方法。获得连接后,使用它就像使用任何其他连接一样。因为jdbc/distCoffeesDB已经与 JNDI 命名服务中的XADataSource对象关联,因此以下代码生成一个可用于分布式事务的Connection对象:

Context ctx = new InitialContext();
DataSource ds = (DataSource)ctx.lookup("jdbc/distCoffeesDB");
Connection con = ds.getConnection();

在连接作为分布式事务的一部分时,对其使用有一些次要但重要的限制。事务管理器控制分布式事务何时开始以及何时提交或回滚;因此,应用程序代码永远不应调用Connection.commitConnection.rollback方法。应用程序也不应调用Connection.setAutoCommit(true),这会启用自动提交模式,因为这也会干扰事务管理器对事务边界的控制。这就解释了为什么在分布式事务范围内创建的新连接默认情况下会禁用其自动提交模式。请注意,这些限制仅适用于连接参与分布式事务时;连接不参与分布式事务时没有限制。

对于以下示例,假设已经发货一份咖啡订单,这将触发对驻留在不同 DBMS 服务器上的两个表进行更新。第一个表是一个新的INVENTORY表,第二个是COFFEES表。因为这些表位于不同的 DBMS 服务器上,涉及它们两个的事务将是一个分布式事务。以下示例中的代码获取连接,更新COFFEES表,并关闭连接,是分布式事务的第二部分。

注意,代码并没有显式提交或回滚更新,因为分布式事务的范围由中间层服务器的基础系统基础设施控制。此外,假设用于分布式事务的连接是一个连接池连接,应用程序使用finally块来关闭连接。这样可以确保即使抛出异常,也会关闭有效连接,从而确保连接被返回到连接池以进行回收利用。

以下代码示例说明了一个企业 Bean,它是一个实现了可以被客户端计算机调用的方法的类。这个示例的目的是演示分布式事务的应用代码与其他代码没有任何不同,只是它不调用Connection方法commitrollbacksetAutoCommit(true)。因此,您不需要担心理解所使用的 EJB API。

import java.sql.*;
import javax.sql.*;
import javax.ejb.*;
import javax.naming.*;
public class DistributedTransactionBean implements SessionBean {
    // ...
    public void ejbCreate() throws CreateException {
        ctx = new InitialContext();
        ds = (DataSource)ctx.lookup("jdbc/distCoffeesDB");
    }
    public void updateTotal(int incr, String cofName, String username,
                            String password)
        throws SQLException {
        Connection con;
        PreparedStatement pstmt;
        try {
            con = ds.getConnection(username, password);
            pstmt = con.prepareStatement("UPDATE COFFEES " +
                        "SET TOTAL = TOTAL + ? " +
                        "WHERE COF_NAME = ?");
            pstmt.setInt(1, incr);
            pstmt.setString(2, cofName);
            pstmt.executeUpdate();
            stmt.close();
        } finally {
            if (con != null) con.close();
        }
    }
    private DataSource ds = null;
    private Context ctx = null;
}

处理 SQLException

原文:docs.oracle.com/javase/tutorial/jdbc/basics/sqlexception.html

本页涵盖以下主题:

  • SQLException 概述
  • 检索异常
  • 检索警告
  • 分类的 SQLException
  • SQLException 的其他子类

SQLException 概述

当 JDBC 在与数据源交互过程中遇到错误时,会抛出一个SQLException实例,而不是Exception。(在这种情况下,数据源代表Connection对象连接的数据库。)SQLException实例包含以下信息,可以帮助您确定错误的原因:

  • 错误描述。通过调用方法SQLException.getMessage检索包含此描述的String对象。
  • SQLState 代码。这些代码及其相应的含义已经由 ISO/ANSI 和 Open Group(X/Open)标准化,尽管一些代码已保留供数据库供应商自行定义。这个String对象由五个字母数字字符组成。通过调用方法SQLException.getSQLState检索此代码。
  • 错误代码。这是一个整数值,标识导致抛出SQLException实例的错误。其值和含义是特定于实现的,可能是底层数据源返回的实际错误代码。通过调用方法SQLException.getErrorCode检索错误。
  • 一个原因。SQLException实例可能具有因果关系,由导致抛出SQLException实例的一个或多个Throwable对象组成。要浏览这些原因链,递归调用方法SQLException.getCause,直到返回一个null值。
  • 任何链接异常的引用。如果发生多个错误,这些异常通过此链引用。通过在抛出异常上调用方法SQLException.getNextException检索这些异常。

检索异常

下面的方法,JDBCTutorialUtilities.printSQLException,输出了SQLException中包含的 SQLState、错误代码、错误描述以及原因(如果有的话),以及与之链接的任何其他异常:

public static void printSQLException(SQLException ex) {
    for (Throwable e : ex) {
        if (e instanceof SQLException) {
            if (ignoreSQLException(
                ((SQLException)e).
                getSQLState()) == false) {
                e.printStackTrace(System.err);
                System.err.println("SQLState: " +
                    ((SQLException)e).getSQLState());
                System.err.println("Error Code: " +
                    ((SQLException)e).getErrorCode());
                System.err.println("Message: " + e.getMessage());
                Throwable t = ex.getCause();
                while(t != null) {
                    System.out.println("Cause: " + t);
                    t = t.getCause();
                }
            }
        }
    }
}

例如,如果您使用 Java DB 调用方法CoffeesTable.dropTable,表COFFEES不存在,并且您删除对JDBCTutorialUtilities.ignoreSQLException的调用,输出将类似于以下内容:

SQLState: 42Y55
Error Code: 30000
Message: 'DROP TABLE' cannot be performed on
'TESTDB.COFFEES' because it does not exist.

您可以先检索 SQLState,然后相应地处理 SQLException,而不是打印 SQLException 信息。例如,如果 SQLState 等于代码 42Y55(并且您正在使用 Java DB 作为您的 DBMS),则方法 JDBCTutorialUtilities.ignoreSQLException 返回 true,导致 JDBCTutorialUtilities.printSQLException 忽略 SQLException

public static boolean ignoreSQLException(String sqlState) {
    if (sqlState == null) {
        System.out.println("The SQL state is not defined!");
        return false;
    }
    // X0Y32: Jar file already exists in schema
    if (sqlState.equalsIgnoreCase("X0Y32"))
        return true;
    // 42Y55: Table already exists in schema
    if (sqlState.equalsIgnoreCase("42Y55"))
        return true;
    return false;
}

Java 中文官方教程 2022 版(三十四)(4)https://developer.aliyun.com/article/1488035

相关文章
|
2月前
|
Java 开发工具 Android开发
Kotlin教程笔记(26) -Kotlin 与 Java 共存(一)
Kotlin教程笔记(26) -Kotlin 与 Java 共存(一)
|
3天前
|
移动开发 前端开发 Java
Java最新图形化界面开发技术——JavaFx教程(含UI控件用法介绍、属性绑定、事件监听、FXML)
JavaFX是Java的下一代图形用户界面工具包。JavaFX是一组图形和媒体API,我们可以用它们来创建和部署富客户端应用程序。 JavaFX允许开发人员快速构建丰富的跨平台应用程序,允许开发人员在单个编程接口中组合图形,动画和UI控件。本文详细介绍了JavaFx的常见用法,相信读完本教程你一定有所收获!
Java最新图形化界面开发技术——JavaFx教程(含UI控件用法介绍、属性绑定、事件监听、FXML)
|
17天前
|
NoSQL Java 关系型数据库
Liunx部署java项目Tomcat、Redis、Mysql教程
本文详细介绍了如何在 Linux 服务器上安装和配置 Tomcat、MySQL 和 Redis,并部署 Java 项目。通过这些步骤,您可以搭建一个高效稳定的 Java 应用运行环境。希望本文能为您在实际操作中提供有价值的参考。
95 26
|
24天前
|
安全 Java 编译器
Kotlin教程笔记(27) -Kotlin 与 Java 共存(二)
Kotlin教程笔记(27) -Kotlin 与 Java 共存(二)
|
24天前
|
Java 开发工具 Android开发
Kotlin教程笔记(26) -Kotlin 与 Java 共存(一)
Kotlin教程笔记(26) -Kotlin 与 Java 共存(一)
|
1月前
|
Java 编译器 Android开发
Kotlin教程笔记(28) -Kotlin 与 Java 混编
Kotlin教程笔记(28) -Kotlin 与 Java 混编
33 2
|
23天前
|
Java 数据库连接 编译器
Kotlin教程笔记(29) -Kotlin 兼容 Java 遇到的最大的“坑”
Kotlin教程笔记(29) -Kotlin 兼容 Java 遇到的最大的“坑”
42 0
|
1月前
|
安全 Java 编译器
Kotlin教程笔记(27) -Kotlin 与 Java 共存(二)
Kotlin教程笔记(27) -Kotlin 与 Java 共存(二)
|
1月前
|
Java 开发工具 Android开发
Kotlin教程笔记(26) -Kotlin 与 Java 共存(一)
Kotlin教程笔记(26) -Kotlin 与 Java 共存(一)
|
1月前
|
Java 编译器 Android开发
Kotlin教程笔记(28) -Kotlin 与 Java 混编
Kotlin教程笔记(28) -Kotlin 与 Java 混编