摘要
本文概述了JDBC
和JDBC
驱动程序。其中包括JDBC
的架构和通过一个简单的示例来演示如何连接到数据库并使用PreparedStatements
、事务、连接池等处理SQL
查询和响应。
JDBC 是什么?
JDBC
(即Java
数据库连接)是管理连接到数据库、发出查询和命令以及处理从数据库获得的结果集的Java API
。 JDBC
于1997
年作为JDK 1.1
的一部分发布,是最早为Java
持久层开发的组件之一。
JDBC
最初是被设想为客户端API
,使Java
客户端能够与数据源交互。这在JDCB 2.0
中发生了变化,其中包括一个支持服务器端JDBC
连接的可选包。从那时起,每个新的JDBC
版本都对客户端包(sql语句
) 和服务器端包 (sql.sql
)进行了更新。JDBC 4.3
是撰写本文时的最新版本,于2017
年9
月作为Java SE 9
的一部分发布。
JDBC 的工作原理
JDBC
让我们可以在Java
程序中与数据库进行交互。它充当了从代码到数据库的桥梁。
JDBC 与 ODBC
在JDBC
之前,开发人员是用的开放式数据库连接(ODBC),它是一种与语言无关的标准方法,用于访问关系型数据库管理系统或者RDBMS
。
而JDBC
基于ODBC
,提供了一个编程级接口,用于处理Java
应用程序与数据库或RDBMS
通信。
JDBC 的架构
通过长久以来的改进,JDBC
已经成为一个功能丰富、高性能且可靠的库。其接口包括两部分:
JDBC API
支持Java
应用程序和JDBC
管理器之间的通信,是应用程序代码与之交互的通用API
。
JDBC
驱动程序
支持JDBC
管理器和数据库驱动程序之间的通信,是使用的数据库的JDBC
兼容驱动程序。
作为开发者,我们无需关注其实现,但是需要注意它的几种驱动类型
JDBC-ODBC
桥驱动程序:底层使用ODBC
驱动程序来连接数据库- 本地
API
驱动程序:提供了从Java
到本机数据库客户端的接口 - 网络协议驱动:是依赖于网络服务器上的中间件,提供了
Java
和RDBMS
供应商特定协议之间的通用接口 - 本地协议驱动:是直接在
Java
中实现供应商特定协议的驱动程序
实例:简单的数据库连接和查询
在Java
生态系统中编程的好处之一,就是我们选择的任何数据库找到一个稳定的JDBC
数据库驱动。接下里,我们将通过使用 SQLite(因为它非常易于使用)来了解JDBC
。
使用JDBC
连接数据库的步骤如下:
-
安装或找到要访问的数据库。
-
确保项目中引入
JDBC
库。 -
确保所需的
JDBC
驱动程序在类路径中。 -
使用
JDBC
库获得与数据库的连接。 -
使用该连接来发出
SQL
命令。 -
完成后关闭连接。
下面,我们来实际操作一下。
准备 - 查找 JDBC 驱动程序
查找要使用的数据库的驱动程序,可以对数据库和所需JDBC
驱动进行网络搜索。例如,输入“ mysql jdbc driver”将查询到MySQL
驱动程序。
步骤 1. 下载并安装 SQLite
SQLit
e是一个轻型的数据库,它不太适用于生产用途,但它是快速尝试的绝佳选择。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
来执行此文件。
有了基本的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());
}
}
}
}
编译并执行此代码。假设一切顺利,我们将收到一个肯定的信息。
找不到合适的驱动程序?
如果收到类似
No suitable driver found for jdbc:sqlite
”的错误,则需要检查类路径并确保它指向下载的驱动程序。驱动程序连接失败是使用JDBC的初学者最常见的绊脚石。
现在,我们已经为执行一些SQL
命令做好了准备
步骤 5. 查询数据库
有了实时连接对象,我们可以来做一些有用的事情,例如查询数据库。下面代码展示了如何使用JDBC Connection
和Statement
对象查询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())
。并输出展示结果。
注意,我们还通过调用
conn.close()
来关闭连接。
使用 JDBC 的网络连接:
上面代码中的数据库连接字符串用于本地连接。要通过网络访问数据库,连接字符串(通常)需要包括网络
URL
和用于访问它的凭据。 格式为:jdbc:sqlite:path-to-db-file/chinook/chinook.db
PreparedStatements、批量更新和事务
到目前为止,我们已经介绍了使用JDBC
连接数据库和执行SQL
命令的基础知识。虽然Statements
和ResultSet
适用于常见场景,但对于更大或更复杂的应用程序,可能需要其他选项。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,这意味着无论何时运行executeUpdate
或executeInsert
,命令都会被提交。
如下代码显示了JDBC
事务的一小部分:
connection.setAutoCommit(false);
// Use executeUpdate multiple times
connection.commit();
当connection.commit()
执行,所有包裹在里面的更新操作将被尝试执行,如果有任何失败,他们都将被回滚。
使用 JDBC 的连接池
除了上面介绍的之外。JDBC 4.3
有更多值得探索的特性,包括与DataSource
对象的连接池。
连接池是一种性能改进,是一个根据某些参数重用数据库连接的集合。无需生成、使用和丢弃连接,而是在池中维护它们。下面我们一起看下如何使用JDBC
的DataSource
对象创建连接池。
在上面的示例中,我们使用的是SQLite
来演示,因为SQLite
是一个简单的文件数据库,所以我们并不需要将其连接池化。但它的驱动程序确实提供DataSource
:org.sqlite.javax.SQLiteConnectionPoolDataSource
DataSource
。(注意,并非所有对象实现都支持连接池。)
获取连接池的方法也有多种,包括使用从 Java 命名和目录接口 (JNDI)获得的单独池服务。通常,我们使用应用程序服务器附带的池,例如Tomcat JDBC 连接池。另一种选择是使用第三方工具,例如 C3P0 项目。下面我们将使用C3P0
来作为示例演示。
用于连接池的 C3P0
将C3P0
与SQLite
一起使用相对简单。可以通过配置先前的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
中配置参数可以在不触及源代码的情况下在外部进行修改。
总结
JDBC
是Java
最古老的API
之一,它为Java
应用程序开发的长期需求之一,提供了易于使用的解决方案。只需了解本文中演示的几个JDBC
调用,你就可以开始使用JDBC
连接到几乎任何数据库。一旦你掌握了这些命令,你就可以开始探索一些内置在JDBC
中的更复杂的选项。
虽然JDBC
对于简单的应用程序来说已经足够了,但大多数开发人员最终会使用Java Persistence API (JPA)
来开发更正式的数据访问层。JPA
需要更多的前期工作和对应用程序体系结构的更复杂的理解,但它提供了一个更加一致、隔离和定义良好的数据访问层。如果你有兴趣,可以参考:什么是 JPA?Java Persistence API 简介,了解有关为Java
应用程序开发数据持久层的更多信息。
有关本文的更多内容,请参考:www.infoworld.com/article/338…