什么是 JDBC?Java 数据库连接简介

摘要

本文概述了JDBCJDBC驱动程序。其中包括JDBC的架构和通过一个简单的示例来演示如何连接到数据库并使用PreparedStatements、事务、连接池等处理SQL 查询和响应。

JDBC 是什么?

JDBC(即Java数据库连接)是管理连接到数据库、发出查询和命令以及处理从数据库获得的结果集的Java APIJDBC1997年作为JDK 1.1的一部分发布,是最早为Java持久层开发的组件之一。

JDBC最初是被设想为客户端API,使Java客户端能够与数据源交互。这在JDCB 2.0中发生了变化,其中包括一个支持服务器端JDBC连接的可选包。从那时起,每个新的JDBC版本都对客户端包(sql语句) 和服务器端包 (sql.sql)进行了更新。JDBC 4.3是撰写本文时的最新版本,于20179月作为Java SE 9的一部分发布。

JDBC 的工作原理

JDBC让我们可以在Java程序中与数据库进行交互。它充当了从代码到数据库的桥梁。 image.png

JDBC 与 ODBC

JDBC之前,开发人员是用的开放式数据库连接(ODBC),它是一种与语言无关的标准方法,用于访问关系型数据库管理系统或者RDBMS

JDBC基于ODBC,提供了一个编程级接口,用于处理Java应用程序与数据库或RDBMS通信。

JDBC 的架构

通过长久以来的改进,JDBC已经成为一个功能丰富、高性能且可靠的库。其接口包括两部分:

JDBC API

支持Java应用程序和JDBC管理器之间的通信,是应用程序代码与之交互的通用API

JDBC驱动程序

支持JDBC管理器和数据库驱动程序之间的通信,是使用的数据库的JDBC兼容驱动程序。

作为开发者,我们无需关注其实现,但是需要注意它的几种驱动类型

  • JDBC-ODBC桥驱动程序:底层使用ODBC驱动程序来连接数据库
  • 本地API驱动程序:提供了从Java到本机数据库客户端的接口
  • 网络协议驱动:是依赖于网络服务器上的中间件,提供了JavaRDBMS供应商特定协议之间的通用接口
  • 本地协议驱动:是直接在Java中实现供应商特定协议的驱动程序

实例:简单的数据库连接和查询

Java生态系统中编程的好处之一,就是我们选择的任何数据库找到一个稳定的JDBC数据库驱动。接下里,我们将通过使用 SQLite(因为它非常易于使用)来了解JDBC

使用JDBC连接数据库的步骤如下:

  1. 安装或找到要访问的数据库。

  2. 确保项目中引入JDBC库。

  3. 确保所需的JDBC驱动程序在类路径中。

  4. 使用JDBC库获得与数据库的连接。

  5. 使用该连接来发出SQL命令。

  6. 完成后关闭连接。

下面,我们来实际操作一下。

准备 - 查找 JDBC 驱动程序

查找要使用的数据库的驱动程序,可以对数据库和所需JDBC驱动进行网络搜索。例如,输入“ mysql jdbc driver”将查询到MySQL驱动程序。

步骤 1. 下载并安装 SQLite

SQLite是一个轻型的数据库,它不太适用于生产用途,但它是快速尝试的绝佳选择。SQLite使用文件作为其功能数据库,无需安装任何服务或守护程序。

现在,我们首先下载 SQLite示例数据库。解压缩.db文件并将其保存。该文件包含一个基于功能文件的数据库以及我们可以使用的示例架构和数据。

SQL和JDBC

在过去的十年中,NoSQL变得越来越流行,但是关系数据库仍然是使用中最常见的数据存储类型。一个关系型数据库是由列和行的表的结构化存储库。SQL(结构化查询语言)是数据架构师用于在关系数据库中创建,读取,更新和删除新记录之类的语言。JDBC是从Java到SQL 的适配器层:它为Java开发人员提供了一个公共接口,用于连接数据库,发出查询和命令以及管理响应。

步骤 2. 将 JDBC 导入 Java 应用程序

首先,我们需要为操作系统 安装兼容的 JDK

假设您已安装Java平台开发人员工具,我们可以从创建一个简单的Java程序开始。 创建WhatIsJdbc.java文件, 并将下面代码复制到文件中

  class WhatIsJdbc {
        public static void main(String args[]){
            System.out.println("Hello World");
        }
  }

我们可以通过命令javac WhatIsJdbc.java编译该文件,将得到hatIsJdbc.class 文件。然后我们可以通过命令java WhatIsJdbc来执行此文件。

image.png

有了基本的Java程序,我们就可以引入JDBC库,复制以下代码并贴贴到上面的简单Java程序开头

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.ResultSet;
import java.sql.Statement;

导入这些类的作用:

  • Connection 用于表示与数据库的连接。
  • DriverManager 获取与数据库的连接。(另一个选项是DataSource,用于连接池。)
  • SQLException 处理Java应用程序和数据库之间的SQL错误。
  • ResultSet Statement数据结果集和SQL语句建模。

步骤 3. 将 JDBC 驱动程序添加到程序的类路径

接下里,把SQLite驱动程序添加到类路径中。JDBC 驱动程序是为特定数据库实现JDBC API的类。

GitHub 下载SQLite驱动程序。确保获取最新.jar文件并将其存储在一个你会记得的地方。下次执行Java程序时通过类路径将该.jar文件引入。

设置类路径的方法有几种,下面代码演示了如何使用命令行来做到这一点。

java.exe -classpath /path-to-driver/sqlite-jdbc-3.23.1.jar:. WhatIsJdbc

注意,我们将类路径设置为指向驱动程序和本地目录。这样,Java才会找到我们的类文件

步骤 4. 获取数据库连接

现在,类路径可以访问驱动程序。更改我们简单Java应用程序文件,如下所示:

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.ResultSet;
import java.sql.Statement;
class WhatIsJdbc {
    
    public static void main(String[] args) {
        Connection conn =null;
        try {
            String url = "jdbc:sqlite:C:\\Users\\Administrator\\Desktop\\test\\chinook.db";
            conn = DriverManager.getConnection(url);
            System.out.println("Got it!");

        } catch (SQLException e) {
            throw new Error("Problem", e);
        } finally {
            try {
                if (conn != null) {
                    conn.close();
                }
            } catch (SQLException ex) {
                System.out.println(ex.getMessage());
            }
        }
    }
}

编译并执行此代码。假设一切顺利,我们将收到一个肯定的信息。

image.png

找不到合适的驱动程序?

如果收到类似No suitable driver found for jdbc:sqlite”的错误,则需要检查类路径并确保它指向下载的驱动程序。驱动程序连接失败是使用JDBC的初学者最常见的绊脚石。

现在,我们已经为执行一些SQL命令做好了准备

步骤 5. 查询数据库

有了实时连接对象,我们可以来做一些有用的事情,例如查询数据库。下面代码展示了如何使用JDBC ConnectionStatement对象查询SQLite

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.ResultSet;
import java.sql.Statement;

class WhatIsJdbc {

    public static void main(String[] args) {
        Connection conn = null;
        try {
            String url = "jdbc:sqlite:path-to-db-file/chinook/chinook.db";
            conn = DriverManager.getConnection(url);
            Statement stmt = null;
            String query = "select * from albums";
            try {
                stmt = conn.createStatement();
                ResultSet rs = stmt.executeQuery(query);
                while (rs.next()) {
                    String name = rs.getString("title");
                    System.out.println(name);
                }
            } catch (SQLException e) {
                throw new Error("Problem", e);
            } finally {
                if (stmt != null) {
                    stmt.close();
                }
            }
        } catch (SQLException e) {
            throw new Error("Problem", e);
        } finally {
            try {
                if (conn != null) {
                    conn.close();
                }
            } catch (SQLException ex) {
                System.out.println(ex.getMessage());
            }
        }
    }
}

在上面的代码中,我们使用Connection对象获得一个Statement对象:conn.createStatement()。然后,我们使用此对象执行SQL查询:stmt.executeQuery(query)。该executeQuery命令返回一个ResultSet对象,然后我们用它来对进行数据迭代while (rs.next())。并输出展示结果。

image.png

注意,我们还通过调用conn.close()来关闭连接。

使用 JDBC 的网络连接

上面代码中的数据库连接字符串用于本地连接。要通过网络访问数据库,连接字符串(通常)需要包括网络URL和用于访问它的凭据。 格式为:jdbc:sqlite:path-to-db-file/chinook/chinook.db

PreparedStatements、批量更新和事务

到目前为止,我们已经介绍了使用JDBC连接数据库和执行SQL命令的基础知识。虽然StatementsResultSet适用于常见场景,但对于更大或更复杂的应用程序,可能需要其他选项。JDBC库地不断发展以满足大多数数据库访问需求。

PreparedStatements

一种提高代码灵活性的简单方法是用PreparedStatement替换Statement类。如下代码所示:

String sql = "insert into albums values (?, ?);";
PreparedStatement prepState = connection.prepareStatement(sql);

prepState.setString(1, "Uprising");

prepState.setString(2, "Bob Marley and the Wailers ");

int rowsAffected = preparedStatement.executeUpdate();

上面代码中PreparedStatement用问号(?)替换硬编码值。使用PreparedStatements可优化代码的重用性: PreparedStatement仅被编译一次,然后可以与各种参数一起重用。随着代码库的增长,只需在语句中插入新值即可,而不用修改字符串对象本身。

批量更新

每当应用程序要发布多个更新时,分批执行它们可以极大地提高性能。批处理的实质是获取多个更新并将它们收集在一起,然后一次发布所有更新。如下代码展示了如何使用JDBC的批处理方法来执行几个PreparedStatements的批处理更新:

prepState.setString(1, "Uprising");
prepState.setString(2, "Bob Marley and the Wailers");
preparedStatement.addBatch();

prepState.setString(1, "Wildflowers");
prepState.setString(2, "Tom Petty and the Heartbreakers");
preparedStatement.addBatch();

int[]  rowsAffected   = preparedStatement.executeBatch();

JDBC 事务

关系数据库中的事务允许将一组更新包装在完全成功或失败的交互中。通过JDBC使用事务的基础是告诉系统关闭自动提交,然后在完成后手动告诉系统进行提交。默认情况下,自动提交功能为on,这意味着无论何时运行executeUpdateexecuteInsert,命令都会被提交。 如下代码显示了JDBC事务的一小部分:

connection.setAutoCommit(false);
// Use executeUpdate multiple times
connection.commit();

connection.commit()执行,所有包裹在里面的更新操作将被尝试执行,如果有任何失败,他们都将被回滚。

使用 JDBC 的连接池

除了上面介绍的之外。JDBC 4.3有更多值得探索的特性,包括与DataSource对象的连接池。

连接池是一种性能改进,是一个根据某些参数重用数据库连接的集合。无需生成、使用和丢弃连接,而是在池中维护它们。下面我们一起看下如何使用JDBCDataSource对象创建连接池。

在上面的示例中,我们使用的是SQLite来演示,因为SQLite是一个简单的文件数据库,所以我们并不需要将其连接池化。但它的驱动程序确实提供DataSourceorg.sqlite.javax.SQLiteConnectionPoolDataSourceDataSource。(注意,并非所有对象实现都支持连接池。)

获取连接池的方法也有多种,包括使用从 Java 命名和目录接口 (JNDI)获得的单独池服务。通常,我们使用应用程序服务器附带的池,例如Tomcat JDBC 连接池。另一种选择是使用第三方工具,例如 C3P0 项目。下面我们将使用C3P0来作为示例演示。

用于连接池的 C3P0

C3P0SQLite一起使用相对简单。可以通过配置先前的SQLite数据源或直接在 C3P0中使用数据库规范配置池来完成此操作。我们将尝试更简单的选项。注意,此时我们需要在类路径中包含 C3P0.jar

使用代码如下:

import com.mchange.v2.c3p0.ComboPooledDataSource;
import org.sqlite.javax.SQLiteConnectionPoolDataSource;
import org.sqlite.SQLiteConfig;
import java.sql.Connection;
// …

上面代码显示了可用于调整池的工作方式的多种选项, 例如保持多少连接以及获取它们的增量。上面这个代码只是让我们了解什么是可能的。配置池存储后,JDBC数据库的实际代码使用与以前完全相同。唯一的区别在于构建Connection对象的方式。

JDBC 和 JNDI

对应用程序架构的一个常见改进是将获取数据库和连接池转移到JNDI,在JNDI中配置参数可以在不触及源代码的情况下在外部进行修改。

总结

JDBCJava最古老的API之一,它为Java应用程序开发的长期需求之一,提供了易于使用的解决方案。只需了解本文中演示的几个JDBC调用,你就可以开始使用JDBC连接到几乎任何数据库。一旦你掌握了这些命令,你就可以开始探索一些内置在JDBC中的更复杂的选项。

虽然JDBC对于简单的应用程序来说已经足够了,但大多数开发人员最终会使用Java Persistence API (JPA)来开发更正式的数据访问层。JPA需要更多的前期工作和对应用程序体系结构的更复杂的理解,但它提供了一个更加一致、隔离和定义良好的数据访问层。如果你有兴趣,可以参考:什么是 JPA?Java Persistence API 简介,了解有关为Java应用程序开发数据持久层的更多信息。

有关本文的更多内容,请参考:www.infoworld.com/article/338…