类:JDBC 简介
JDBC API 是 Java API,可以访问任何类型的表格数据,尤其是存储在Relational Database.中的数据
JDBC 帮助您编写 Java 应用程序来 管理 以下三种编程活动:
下面的简单代码片段给出了这三个步骤的简单示例:
public void connectToAndQueryDatabase(String username, String password) {
Connection con = DriverManager.getConnection(
"jdbc:myDriver:myDatabase",
username,
password);
Statement stmt = con.createStatement();
ResultSet rs = stmt.executeQuery("SELECT a, b, c FROM Table1");
while (rs.next()) {
int x = rs.getInt("a");
String s = rs.getString("b");
float f = rs.getFloat("c");
}
}
这个简短的代码片段实例化一个DriverManager
对象以连接到数据库驱动程序并登录数据库,实例化一个Statement
对象,该对象将您的 SQL 语言查询传递给数据库;实例化一个ResultSet
对象,该对象检索查询的结果,并执行一个简单的while
循环,该循环检索并显示这些结果。就这么简单。
JDBC 产品组件
JDBC 包含四个组件:
- JDBC API — JDBC™API 提供对 Java™编程语言中的关系数据的编程访问。使用 JDBC API,应用程序可以执行 SQL 语句,检索结果并将更改传播回基础数据源。 JDBC API 还可以在分布式异构环境中与多个数据源进行交互。
JDBC API 是 Java 平台的一部分,该 Java 平台包括 Java™标准版(Java™SE)和 Java™企业版(Java™EE)。 JDBC 4.0 API 分为两个软件包:java.sql
和javax.sql.
Java SE 和 Java EE 平台都包含这两个软件包。
- JDBC 驱动程序 管理 器 — JDBC
DriverManager
类定义了可以将 Java 应用程序连接到 JDBC 驱动程序的对象。传统上,DriverManager
是 JDBC 体系结构的骨干。它非常小而简单。
标准扩展包javax.naming
和javax.sql
使您可以使用在 Java 命名和目录interface TM(JNDI)命名服务中注册的DataSource
对象来构建与数据源的连接。可以使用任何一种连接机制,但是建议尽可能使用DataSource
对象。
-
JDBC 测试套件 -JDBC 驱动程序测试套件可帮助您确定 JDBC 驱动程序将运行您的程序。这些测试不是全面的或详尽的,但是它们确实行使了 JDBC API 中的许多重要功能。
-
JDBC-ODBCbridge — Java 软件 bridge 通过 ODBC 驱动程序提供 JDBC 访问。请注意,您需要将 ODBC 二进制代码加载到使用该驱动程序的每台 Client 端计算机上。因此,ODBC 驱动程序最适合企业网络,在该网络中 Client 端安装不是主要问题,或者最适合三层体系结构中用 Java 编写的应用程序服务器代码。
此跟踪使用这四个 JDBC 组件中的前两个来连接到数据库,然后构建一个使用 SQL 命令与测试关系数据库进行通信的 Java 程序。最后两个组件在专门的环境中用于测试 Web 应用程序,或与支持 ODBC 的 DBMS 通信。
JDBC Architecture
两层和三层处理模型
JDBC API 支持用于数据库访问的两层和三层处理模型。
图 1:数据访问的两层体系结构。
在两层模型中,Java applet 或应用程序直接与数据源对话。这需要可以与正在访问的特定数据源进行通信的 JDBC 驱动程序。用户的命令将传递到数据库或其他数据源,并且这些语句的结果将发送回用户。数据源可能位于用户通过网络连接到的另一台机器上。这被称为 Client 端/服务器配置,其中用户的计算机为 Client 端,而容纳数据源的计算机为服务器。该网络可以是 Intranet,例如,它可以连接公司内的员工,也可以是 Internet。
在三层模型中,命令被发送到服务的“中间层”,然后该服务将命令发送到数据源。数据源处理命令,并将结果发送回中间层,然后由中间层将其发送给用户。 MIS 主管发现三层模型非常有吸引力,因为中间层可以保持对访问权限的控制以及可以对公司数据进行的各种更新。另一个优点是它简化了应用程序的部署。最后,在许多情况下,三层体系结构可以提供性能优势。
图 2:数据访问的三层体系结构。
直到最近,中间层经常使用诸如 C 或 C 之类的语言编写,它们提供了快速的性能。但是,随着优化编译器的引入,这些编译器将 Java 字节码转换为高效的机器特定代码和诸如 Enterprise JavaBeans™之类的技术,Java 平台正迅速成为中间层开发的标准平台。这是一个很大的优点,可以利用 Java 的健壮性,多线程和安全性功能。
随着企业越来越多地使用 Java 编程语言编写服务器代码,在三层体系结构的中间层中越来越多地使用 JDBC API。使 JDBC 成为服务器技术的一些功能是它对连接池,分布式事务和断开连接的行集的支持。 JDBC API 还允许从 Java 中间层访问数据源。
关系数据库概述
数据库是以一种可以从中检索信息的方式存储信息的方法。用最简单的术语来说,关系数据库是一种在具有行和列的表中呈现信息的数据库。从某种意义上说,表是关系的集合,它是相同类型(行)的对象的集合。表中的数据可以根据通用键或概念进行关联,并且从表中检索相关数据的能力是术语关系数据库的基础。数据库 管理 系统(DBMS)处理数据的存储,维护和检索方式。对于关系数据库,关系数据库 管理 系统(RDBMS)执行这些任务。本书中使用的 DBMS 是一个通用术语,其中包括 RDBMS。
Integrity Rules
关系表遵循某些完整性规则,以确保其中包含的数据保持准确并始终可访问。首先,关系表中的行应全部不同。如果存在重复的行,则可能无法解决两个可能的选择中的一个是正确的选择。对于大多数 DBMS,用户可以指定不允许重复的行,如果这样做,则 DBMS 将阻止添加任何与现有行重复的行。
传统关系模型的第二个完整性规则是,列值一定不能是重复的组或数组。数据完整性的第三个方面涉及空值的概念。数据库通过使用空值指示缺少值来处理数据可能不可用的情况。它不等于空白或零。一个空白被视为等于另一个空白,一个零等于另一个零,但两个空值不被视为相等。
当表中的每一行都不同时,可以使用一个或多个列来标识特定行。此唯一列或一组列称为主键。属于主键的任何列都不能为 null。如果是这样,则包含它的主键将不再是完整的标识符。此规则称为实体完整性。
Employees
表说明了其中一些关系数据库概念。它有五列和六行,每行代表一个不同的员工。
Employees
桌
Employee_Number | First_name | Last_Name | Date_of_Birth | Car_Number |
---|---|---|---|---|
10001 | Axel | Washington | 28-Aug-43 | 5 |
10083 | Arvid | Sharma | 24-Nov-54 | null |
10120 | Jonas | Ginsberg | 01-Jan-69 | null |
10005 | Florence | Wojokowski | 04-Jul-71 | 12 |
10099 | Sean | Washington | 21-Sep-66 | null |
10035 | Elizabeth | Yamaguchi | 24-Dec-59 | null |
该表的主键通常是员工号,因为保证每个人都不同。 (进行比较时,数字也比字符串 更有效.)也可以使用First_Name
和Last_Name
,因为两者的组合也只能标识示例数据库中的一行。仅使用姓氏是行不通的,因为有两个员工的姓氏为“ Washington”。在这种特殊情况下,名字全都不同,因此可以想象使用该列作为主键,但是最好避免使用可能发生重复的列。如果 Elizabeth Yamaguchi 在这家公司找到工作,并且主键是First_Name
,则 RDBMS 将不允许添加她的名字(如果已指定不允许重复)。因为表中已经有一个伊丽莎白,所以添加第二个伊丽莎白会使主键无用,因为它只能识别一行。请注意,尽管对于此示例,使用First_Name
和Last_Name
是唯一的复合键,但在较大的数据库中可能并非唯一。另请注意,Employee
表假定每个员工只能有一辆汽车。
SELECT 语句
SQL 是一种设计用于关系数据库的语言。有一组被认为是标准的基本 SQL 命令,并且所有 RDBMS 都使用这些命令。例如,所有 RDBMS 都使用SELECT
语句。
SELECT
语句(也称为查询)用于从表中获取信息。它指定一个或多个列标题,要从中选择的一个或多个表以及一些选择条件。 RDBMS 返回满足规定要求的列条 Object 行。诸如此类的SELECT
语句将获取拥有公司汽车的员工的名字和姓氏:
SELECT First_Name, Last_Name
FROM Employees
WHERE Car_Number IS NOT NULL
结果集(满足Car_Number
列中不包含 null 的要求的行集)如下。因为SELECT
语句(第一行)指定了First_Name
和Last_Name
列,所以满足要求的每一行都会打印出名字和姓氏。 FROM
子句(第二行)提供了从中选择列的表。
FIRST_NAME | LAST_NAME |
---|---|
Axel | Washington |
Florence | Wojokowski |
下面的代码生成的结果集包含整个表,因为它要求无限制(没有WHERE
子句)的表 employees 中的所有列。请注意,SELECT *
表示“选择所有列”。
SELECT *
FROM Employees
WHERE Clauses
SELECT
语句中的WHERE
子句提供了选择值的条件。例如,在下面的代码片段中,仅当值出现在 Last_Name 列以字符串'Washington'开头的行中时,才选择值。
SELECT First_Name, Last_Name
FROM Employees
WHERE Last_Name LIKE 'Washington%'
关键字LIKE
用于比较字符串,它具有可以使用包含通配符的 Pattern 的功能。例如,在上面的代码片段中,“华盛顿”的末尾有一个百分号(%
),表示任何包含字符串“华盛顿”加上零个或多个其他字符的值都将满足此选择条件。因此,“华盛顿”或“华盛顿”将是匹配项,但“洗涤”将不是匹配项。 LIKE
子句中使用的另一个通配符是下划线(_
),代表任何一个字符。例如,
WHERE Last_Name LIKE 'Ba_man'
会匹配“蝙蝠侠”,“巴尔曼”,“蝙蝠侠”,“巴尔曼”,“蝙蝠侠”,“蝙蝠侠”,等等。
下面的代码片段具有WHERE
子句,该子句使用等号(=)比较数字。它选择分配了汽车 12 的员工的名字和姓氏。
SELECT First_Name, Last_Name
FROM Employees
WHERE Car_Number = 12
下一个代码片段选择员工号大于 10005 的员工的名字和姓氏:
SELECT First_Name, Last_Name
FROM Employees
WHERE Employee_Number > 10005
WHERE
子句可以很复杂,有多个条件,在某些 DBMS 中有嵌套条件。本概述不会涵盖复杂的WHERE
子句,但是下面的代码片段具有一个WHERE
子句,其中有两个条件。此查询选择雇员姓名小于 10100 且没有公司汽车的雇员的名字和姓氏。
SELECT First_Name, Last_Name
FROM Employees
WHERE Employee_Number < 10100 and Car_Number IS NULL
WHERE
子句是一种特殊的类型,它涉及联接,下一节将对此进行说明。
Joins
关系数据库的一个显着 Feature 是,可以从称为联接的多个表中获取数据。假设在检索了拥有公司汽车的雇员的姓名之后,一个人想要找出谁拥有哪辆汽车,包括品牌,型号和年份。此信息存储在另一个表Cars
中:
Cars
桌
Car_Number | Make | Model | Year |
---|---|---|---|
5 | Honda | Civic DX | 1996 |
12 | Toyota | Corolla | 1999 |
两个表中必须存在一列,以便将它们彼此关联。该列必须是一个表中的主键,在另一表中称为外键。在这种情况下,两个表中出现的列是Car_Number
,这是表Cars
的主键和表 Employees 中的外键。如果将 1996 年的本田思域(Honda Civic)破坏并从Cars
表中删除,则也必须从 Employees 表中删除Car_Number
5,以保持所谓的参照完整性。否则,Employees
表中的外键列(Car_Number
)将包含一个未引用Cars
中任何内容的条目。外键必须为 null 或等于它所引用表的现有主键值。这不同于主键,主键不能为空。表Employees
的Car_Number
列中有多个空值,因为员工可能没有公司的车。
以下代码要求拥有公司汽车的员工的名字和姓氏,以及这些汽车的品牌,型号和年份。请注意,FROM
子句同时列出了 employees 和 Cars,因为请求的数据都包含在两个表中。使用表名和列名前的点(.)表示哪个表包含该列。
SELECT Employees.First_Name, Employees.Last_Name,
Cars.Make, Cars.Model, Cars.Year
FROM Employees, Cars
WHERE Employees.Car_Number = Cars.Car_Number
这将返回一个类似于以下内容的结果集:
FIRST_NAME | LAST_NAME | MAKE | MODEL | YEAR |
---|---|---|---|---|
Axel | Washington | Honda | Civic DX | 1996 |
Florence | Wojokowski | Toyota | Corolla | 1999 |
常用 SQL 命令
SQL 命令分为几类,主要的两个是数据操作语言(DML)命令和数据定义语言(DDL)命令。 DML 命令处理数据,可以检索数据或对其进行修改以使其保持最新状态。 DDL 命令创建或更改表以及其他数据库对象,例如视图和索引。
以下是更常见的 DML 命令的列表:
-
SELECT —
用于查询和显示数据库中的数据。SELECT
语句指定要包含在结果集中的列。应用程序中使用的绝大多数 SQL 命令都是SELECT
语句。 -
INSERT —
将新行添加到表中。INSERT
用于填充新创建的表或向现有表添加新的一行。 -
DELETE —
从表中删除指定的一行或一组行 -
UPDATE —
更改表中一列或一组列中的现有值
更常见的 DDL 命令如下:
-
CREATE TABLE —
使用用户提供的列名创建一个表。用户还需要为每一列中的数据指定一种类型。数据类型从一个 RDBMS 到另一个 RDBMS,所以用户可能需要使用元数据来构建特定数据库使用的数据类型。通常,CREATE TABLE
的使用频率比数据操作命令要少,这是因为表仅创建一次,而添加或删除行或更改单个值通常会更频繁地发生。 -
DROP TABLE —
删除所有行,并从数据库中删除表定义。需要 JDBC API 实现来支持 SQL92 过渡级别所指定的DROP TABLE
命令。但是,对DROP TABLE
的CASCADE
和RESTRICT
选项的支持是可选的。另外,当存在引用所删除表的视图或完整性约束时,DROP TABLE
的行为由实现定义。 -
ALTER TABLE —
从表中添加或删除列。它还添加或删除表约束并更改列属性
结果集和游标
满足查询条件的行称为结果集。结果集中返回的行数可以为零,一或很多。用户可以一次访问一行结果集中的数据,而游标提供了这样做的手段。游标可被视为指向包含结果集各行的文件的指针,并且该指针具有跟踪当前正在访问的行的能力。游标允许用户从上到下处理结果集的每一行,因此可以用于迭代处理。大多数 DBMS 在生成结果集时会自动创建一个游标。
较早的 JDBC API 版本为结果集的游标添加了新功能,从而使其可以向前和向后移动,还可以将其移动到指定的行或位置相对于另一行的行。
Transactions
当一个用户访问数据库中的数据时,另一用户可能同时访问同一数据。例如,如果第一位用户正在同时更新表中的某些列,而第二位用户正在从同一表中选择列,则第二位用户有可能获得部分旧数据和部分更新数据。因此,DBMS 使用事务将数据维持在一致状态(数据一致性),同时允许多个用户同时访问数据库(数据并发)。
事务是组成逻辑工作单元的一组一个或多个 SQL 语句。事务以提交还是回滚结束,具体取决于数据一致性或数据并发性是否存在问题。 commit 语句使事务中的 SQL 语句导致的更改永久生效,而 rollback 语句撤消事务中的 SQL 语句导致的所有更改。
锁是一种机制,它禁止两个事务同时处理相同的数据。例如,如果表锁上有未提交的事务,则表锁可防止该表被删除。在某些 DBMS 中,表锁还锁定表中的所有行。行锁可防止两个事务修改同一行,或者防止一个事务选择一行而另一事务仍在修改它。
Stored Procedures
存储过程是一组可以按名称调用的 SQL 语句。换句话说,它是可执行代码,是一个微型程序,它执行特定的任务,可以像调用函数或方法一样调用该任务。传统上,存储过程是使用 DBMS 特定的编程语言编写的。最新一代的数据库产品允许使用 Java 编程语言和 JDBC API 编写存储过程。用 Java 编程语言编写的存储过程是 DBMS 之间可移植的字节码。编写存储过程后,就可以使用和重用它,因为支持存储过程的 DBMS 顾名思义会将其存储在数据库中。
以下代码是如何使用 Java 编程语言创建非常简单的存储过程的示例。请注意,存储过程只是一个包含常规 JDBC 代码的静态 Java 方法。它接受两个 Importing 参数,并使用它们来更改员工的车号。
如果您现在不理解该示例,请不要担心。下面的代码示例仅用于说明存储过程的外观。您将在随后的教程中学习如何在此示例中编写代码。
import java.sql.*;
public class UpdateCar {
public static void UpdateCarNum(int carNo, int empNo)
throws SQLException {
Connection con = null;
PreparedStatement pstmt = null;
try {
con = DriverManager.getConnection(
"jdbc:default:connection");
pstmt = con.prepareStatement(
"UPDATE EMPLOYEES " +
"SET CAR_NUMBER = ? " +
"WHERE EMPLOYEE_NUMBER = ?");
pstmt.setInt(1, carNo);
pstmt.setInt(2, empNo);
pstmt.executeUpdate();
}
finally {
if (pstmt != null) pstmt.close();
}
}
}
Metadata
数据库存储用户数据,并且它们还存储有关数据库本身的信息。大多数 DBMS 都有一组系统表,这些系统表列出了数据库中的表,每个表中的列名,主键,外键,存储过程等。每个 DBMS 都有其自己的功能来获取有关表布局和数据库功能的信息。 JDBC 提供了interfaceDatabaseMetaData
,驱动程序编写者必须实现该interfaceDatabaseMetaData
,以便其方法返回有关驱动程序和/或为其编写驱动程序的 DBMS 的信息。例如,大量方法返回驱动程序是否支持特定功能。该interface为用户和工具提供了一种获取元数据的标准化方法。
通常,编写工具和驱动程序的开发人员最可能与元数据有关。