连接 JDBC

ij 工具连接 Apache Derby 数据库并与之进行交互,从而演示了许多数据库概念。可以让 Java 应用程序使用 JDBC 应用程序编程接口(API)连接嵌入式 Apache Derby 数据库并与之进行交互。在接下来的几篇文章中,您将学习如何通过编写自己的 Java 应用程序来重现 ijjava.sql 包中,如果仔细观察,您会发现这个 API 主要由接口组成。因此,创建数据库 JDBC 驱动程序的实际工作由数据库厂商(或第三方)负责,他们必须提供实现这些接口的 Java 类。javax.sql

关于 JDBC 还有最后一点要注意:Java 应用程序和数据库之间的连接由 JDBC 驱动程序控制。原来有 4 种 JDBC 驱动程序类型,由它们的类型号区分:1、2、3 或 4。类型与 Java 应用程序和数据库进行通信所用的技术对应。当今的大多数驱动程序(包括用来连接 Derby 数据库的驱动程序)是 Type 4 驱动程序,这意味着它们是完全用 Java 语言编写的,它们直接将 JDBC API 转换为厂商特定的数据库协议。对于 Derby 数据库,这个过程就更简单了,因为 Derby 是用 Java 语言编写的!

Apache Derby 和 JDBC

既然您熟悉了 JDBC 的基本概念,就可以开始学习如何使用 Java 编程语言连接嵌入式 Apache Derby 数据库。但是首先,必须安装并运行 Apache Derby 软件,其过程见 系列的第一篇文章。如果还没有执行这个关键的步骤,那么请阅读第一篇文章并下载和安装 Derby 软件。在安装 Derby 数据库软件之后,可以使用本文附带的示例代码连接 Derby 数据库,见清单 1。

清单 1. 执行示例代码

rb$ mkdir derbyWork
rb$ cd derbyWork/
rb$ unzip ../derby9.zip 
Archive:  ../derby9.zip
  inflating: FirstConnect.java       
rb$ ls    
FirstConnect.java
rb$ javac FirstConnect.java 
rb$ java FirstConnect 

----------------------------------------------------
Database Name    = Apache Derby
Database Version = 10.1.2.1
Driver Name      = Apache Derby Embedded JDBC Driver
Driver Version   = 10.1.2.1
Database URL     = jdbc:derby:test
----------------------------------------------------
rb$ java FirstConnect
SQLWarning: State=01J01, Severity = 10000
Database 'test' not created, connection made to existing database instead.

----------------------------------------------------
Database Name    = Apache Derby
Database Version = 10.1.2.1
Driver Name      = Apache Derby Embedded JDBC Driver
Driver Version   = 10.1.2.1
Database URL     = jdbc:derby:test
----------------------------------------------------
rb$ ls
FirstConnect.class      derby.log
FirstConnect.java       test

如果数据库无法运行,应该怎么办

CLASSPATH 中。例如,如果代码已经编译了,但是在尝试运行它时出现错误消息 JDBC Driver org.apache.derby.jdbc.EmbeddedDriver not found in CLASSPATH,那么就需要将 derby.jar 文件添加到 CLASSPATH 中。本系列中的第一篇文章 讨论了这个问题。其他错误可能是由于忘了使用 javac

lsFirstConnect 类的 main 方法,生成与这里相似的输出;如果在这个过程中遇到了问题,请参见边栏 如果数据库无法运行,应该怎么办。这段 Java 代码首先使用 Apache Derby 嵌入式 JDBC 驱动程序创建 test

Derby Developer's Guide(可以通过本文的 参考资料 部分中 Derby 在线手册的链接找到这个文档)。

回页首

连接 Java 应用程序和 Derby 数据库

前一节中执行的 Java 代码非常简单,本文的其余部分将详细讨论这些代码。在生产环境中,开发 Java 数据库应用程序可能很困难。本文并不涉及这些细节(这些将在以后的文章中讨论),而是主要关注在 Java 应用程序和嵌入式 Apache Derby 数据库之间建立连接的最基本的技术。这种技术(见清单 2)需要使用 JDBC 驱动程序实现连接协议。

清单 2. 使用 JDBC 连接 Derby 数据库

private static final String driver = "org.apache.derby.jdbc.EmbeddedDriver" ;
private static final String url = "jdbc:derby:test;create=true" ;
 
public static void main(String[] args) {
    Connection con = null ;
    DatabaseMetaData dbmd = null ;
        
    try {
        Class.forName(driver) ;
        con = DriverManager.getConnection(url);
       
       // Use the database connection somehow.
       
    } catch (SQLException se) {
        printSQLException(se) ;
    } catch(ClassNotFoundException e){
        System.out.println("JDBC Driver " + driver + " not found in CLASSPATH") ;
    }finally {
        if(con != null){
            try{
                con.close() ;
            } catch(SQLException se){
                printSQLException(se) ;
            }
        }
    }
}

DataSource

DataSource,这样就可以对数据库连接的细节进行抽象,比如数据库 URL、驱动程序类、用户名、密码和数据库名称。通过对特定的连接信息进行抽象,就很容易修改这些参数而不需要修改应用程序代码。以后的文章将讲解如何对 Derby 使用 DataSource;但是首先了解 JDBC 驱动程序和 DriverManager,然后再了解DataSource,这样更容易理解 JDBC 的基础知识。

ij 工具发出 connect 命令时使用的 URL 完全相同。赋给 drivermain 方法中;它使用 JVM 中的默认类装载器寻找并实例化前面定义的 driver 类(通过使用 Class.forname 方法)。这个方法寻找 Derby 嵌入式驱动程序的类文件(这个文件必须在 CLASSPATH 中存在),然后将它装载到 JVM 中。在装载过程中,会处理这个类的静态部分,这会向 JDBC DriverManagerDriverManager 作为一个工厂对象。给出一个数据库 URL,DriverManager 就会使用适当的 JDBC 驱动程序返回一个数据库连接。这个步骤在下一行代码中执行,在这里使用前面定义的 URL 从 DriverManager 请求一个 JDBC connection。try ... catch 块中,而且在 finally 块中在连接上调用 close 方法。本文后面的 当出现错误时 一节会讨论这两个主题。由于篇幅的限制,本文无法全面讨论用 JDBC 建立数据库连接的不同方式。例如,这个示例没有使用任何安全信息,比如用户名或密码。以后的文章将研究其他数据库连接技术(参见边栏 关于 DataSource)。如果想提前了解这些技术,那么请参考本文 参考资料 部分链接的 Derby Developer's Guide。

数据库元数据

元数据(Metadata) 就是描述数据的数据。在数据库的上下文中,元数据描述特定的数据库,比如数据库的名称、它的版本号或建立连接的 JDBC 驱动程序的名称。通过调用DatabaseMetaData

清单 3. 操作数据库元数据

DatabaseMetaData dbmd = null ;
dbmd = con.getMetaData() ;

System.out.println("\n----------------------------------------------------") ;
System.out.println("Database Name    = " + dbmd.getDatabaseProductName()) ;
System.out.println("Database Version = " + dbmd.getDatabaseProductVersion()) ;
System.out.println("Driver Name      = " + dbmd.getDriverName()) ;
System.out.println("Driver Version   = " + dbmd.getDriverVersion()) ;
System.out.println("Database URL     = " + dbmd.getURL()) ;
System.out.println("----------------------------------------------------") ;

Connection 创建一个新的 DatabaseMetaData 对象。然后,可以调用 Apache Derby JDBC 驱动程序提供的许多元数据函数(完整的列表参见本文 参考资料 链接的 JDBC 规范)。在这个示例中,检索了正在访问的数据库的名称和产品版本号、用来访问数据库的 JDBC 驱动程序的名称和版本号以及标识已经连接到的数据库的完整 JDBC URL。如果要开发的数据库应用程序必须与许多不同的数据库或 JDBC 驱动程序进行交互,那么数据库元数据是最有用的。在这种情况下,可以使用元数据在运行时判断特定数据库和 JDBC 驱动程序的能力。

回页首

出现错误时

在操作 Apache Derby 数据库时(无论是按照本系列以前文章的描述,还是采用自己的方法),您肯定遇到过数据库警告和数据库错误。由于在数据库中存储的信息的重要性,适当地处理数据库错误和警告对于业务是很重要的。幸运的是,正如本节其余部分所演示的,这并不困难。第一步是学习如何查看提供给应用程序的关于数据库遇到的错误的信息,见清单 4。

清单 4. 异常处理代码

private static void printSQLException(SQLException se) {
    while(se != null) {
        System.out.print("SQLException: State:   " + se.getSQLState());
        System.out.println("Severity: " + se.getErrorCode());
        System.out.println(se.getMessage());            
        
        se = se.getNextException();
    }
 }

private static void printSQLWarning(SQLWarning sw) {
    while(sw != null) {
        System.out.print("SQLWarning: State=" + sw.getSQLState()) ;
        System.out.println(", Severity = " + sw.getErrorCode()) ;
        System.out.println(sw.getMessage()); 
        
        sw = sw.getNextWarning();
    }
}

如 清单 4 所示,处理 SQL 异常和 SQL 警告是相似的。SQLWarning 类继承自 SQLException 类;但是 SQL 警告不如 SQL 异常那么严重,所以更好的方法是分别处理它们。这两个函数都声明为 private static,因此可以从 main

在这两个函数中,循环处理警告或异常。这在一般的错误处理场景中可能有点儿奇怪,但是在涉及数据库编程时就很常见了。出现多个警告或异常的原因很简单:在数据库中事件常常连接在一起。例如,如果在数据库中插入多行,而且它们都由于列数据类型或名称不匹配而无法插入,那么就会有多个错误。为了正确地考虑所有错误,需要能够将异常连接在一起,从而让进行调用的代码知道数据库遇到的所有问题。为了简化,Apache Derby 总是将最重要的异常放在异常链的开头。

00000 值就表示成功,类值为 01try ... catch

清单 5. 捕获 SQL 异常

try{
    // Execute a JDBC operation
} catch (SQLException se) {
    printSQLException(se) ;
}

SQLException 对象传递给前面定义的 printSQLException 方法来处理任何 SQL 异常,这会处理所有错误报告。在处理 SQL 异常时,惟一的难点是正确地回收数据库资源,比如数据库连接。因为可以在运行数据库应用程序代码的 JVM 之外管理连接或数据库游标这样的资源,所以应用程序必须显式地关闭这些资源。Connection 对象的 close 方法可以抛出 SQLException,所以要将这个方法放在finallygetWarnings

清单 6. 检查 SQL 警告

SQLWarning swarn = con.getWarnings() ;

if(swarn != null){
    printSQLWarning(swarn) ;
}

printSQLWarning 方法处理。在调用这个方法之前,首先要检查是否至少有一个 SQL 警告;如果有,就将第一个 SQLWarning

回页首

将所有代码组合起来

到目前为止,本文已经讨论了使用 JDBC API 和 Apache Derby 建立数据库连接所需的许多组件。清单 7 中提供了完整的连接示例,它说明这些组件组合在一起可以提供大多数必要的功能。

清单 7. 完整的连接示例代码

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.SQLWarning;
import java.sql.DatabaseMetaData;

public class FirstConnect {
    private static final String driver = "org.apache.derby.jdbc.EmbeddedDriver" ;
    private static final String url = "jdbc:derby:test;create=true" ;
    
    static void printSQLException(SQLException se) {
        while(se != null) {

            System.out.print("SQLException: State:   " + se.getSQLState());
            System.out.println("Severity: " + se.getErrorCode());
            System.out.println(se.getMessage());            
            
            se = se.getNextException();
        }
    }
        
    static void printSQLWarning(SQLWarning sw) {
        while(sw != null) {

            System.out.print("SQLWarning: State=" + sw.getSQLState()) ;
            System.out.println(", Severity = " + sw.getErrorCode()) ;
            System.out.println(sw.getMessage()); 
            
            sw = sw.getNextWarning();
        }
    }

    public static void main(String[] args) {
        Connection con = null ;
        DatabaseMetaData dbmd = null ;
        
        try {
            Class.forName(driver) ;
            con = DriverManager.getConnection(url);

            SQLWarning swarn = con. getWarnings() ;
            
            if(swarn != null){
                printSQLWarning(swarn) ;
            }
            
            dbmd = con.getMetaData() ;
            
            System.out.println("\n----------------------------------------------------") ;
            System.out.println("Database Name    = " + dbmd.getDatabaseProductName()) ;
            System.out.println("Database Version = " + dbmd.getDatabaseProductVersion()) ;
            System.out.println("Driver Name      = " + dbmd.getDriverName()) ;
            System.out.println("Driver Version   = " + dbmd.getDriverVersion()) ;
            System.out.println("Database URL     = " + dbmd.getURL()) ;
            System.out.println("----------------------------------------------------") ;
            
        } catch (SQLException se) {
            printSQLException(se) ;
        } catch(ClassNotFoundException e){
            System.out.println("JDBC Driver " + driver + " not found in CLASSPATH") ;
        }
        finally {
            if(con != null){
                try{
                    con.close() ;
                } catch(SQLException se){
                    printSQLException(se) ;
                }
            }
        }
    }
}

import java.sql.*,但是单独列出每个 import 语句可以明确地表明在应用程序中要使用哪些 JDBC 对象。 这个应用程序包含 清单 4 中的错误处理函数。所有数据库操作都包围在一个 try ... catch 块中,包括 清单 3 所示的数据库元数据操作。许多数据库应用程序都避免数据库应用程序代码和数据库之间的这种耦合,从而简化应用程序和数据库的管理和维护。但是由于 Apache Derby 数据库的嵌入式功能,应用程序和数据库之间的联系比较模糊,并不需要严格实施隔离。

结束语

在本文中,您学习了如何编写连接到嵌入式 Apache Derby 数据库的 Java 应用程序。这个过程使用 Apache Derby 项目中的 Type 4 JDBC 驱动程序连接数据库,从数据库中提取元数据,然后关闭数据库连接。还学习了如何在 Java 应用程序中处理 Derby 可能产生的 SQL 警告和 SQL 异常。以后的文章将以本文介绍的技术为基础,向 Derby 数据库发出查询并在 Java 应用程序中处理结果。