此Java教程适用于JDK 8。本页中描述的示例和实践不利用后续版本中引入的改进,并可能使用不再可用的技术。
有关Java SE 9及后续版本中更新的语言特性的摘要,请参阅Java语言更改。
有关所有JDK版本的新功能、增强功能和已删除或弃用选项的信息,请参阅JDK发行说明。
数据库是一种以能够检索信息的方式存储信息的手段。简单来说,关系型数据库是以行和列的形式呈现信息的一种数据库。表在这里被称为关系,因为它是相同类型对象(行)的集合。表中的数据可以根据共同的键或概念进行关联,从表中检索相关数据是关系型数据库这个术语的基础。数据库管理系统(DBMS)处理数据的存储、维护和检索方式。在关系型数据库的情况下,关系型数据库管理系统(RDBMS)执行这些任务。在本书中,DBMS是一个包括RDBMS在内的通用术语。
关系表遵循一定的完整性规则,以确保它们所包含的数据保持准确并始终可访问。首先,关系表中的行应该是唯一的。如果有重复的行,解决哪个是正确选择的问题可能会出现问题。对于大多数DBMS,用户可以指定不允许重复行,如果这样做,DBMS将阻止添加任何重复现有行的行。
传统关系模型的第二个完整性规则是列值不能是重复组或数组。数据完整性的第三个方面涉及到空值的概念。数据库通过使用空值来处理数据可能不可用的情况,表示值缺失。它不等同于空白或零。空白被认为等于另一个空白,零被认为等于另一个零,但两个空值不被认为是相等的。
当表中的每一行都不同时,可以使用一个或多个列来标识特定的行。这个唯一的列或列组被称为主键。任何作为主键的列都不能为null;如果是null,包含它的主键将不再是一个完整的标识符。这个规则被称为实体完整性。
Employees
表演示了一些这些关系数据库的概念。它有五列和六行,每一行代表一个不同的员工。
员工编号 |
名 |
姓 |
出生日期 |
汽车编号 |
---|---|---|---|---|
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
,如果已经规定不允许重复,关系数据库管理系统将不允许添加她的名字。因为表中已经有一个Elizabeth,再添加一个将使得主键无法用于唯一标识一行。请注意,尽管在本示例中使用First_Name
和Last_Name
作为唯一的组合主键,但在更大的数据库中可能不是唯一的。还请注意,Employees
表假设每个员工只能有一辆汽车。
SELECT
语句SQL是一种设计用于关系数据库的语言。有一组基本的SQL命令被视为标准,并被所有关系数据库管理系统使用。例如,所有关系数据库管理系统都使用SELECT
语句。
SELECT
语句,也称为查询语句,用于从表中获取信息。它指定一个或多个列标题,一个或多个要选择的表,以及一些选择的条件。关系数据库管理系统返回满足所述要求的列条目的行。以下是一个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 |
以下代码生成了一个包含整个表的结果集,因为它要求获取表 Employees 中的所有列且没有限制条件(没有 WHERE 子句)。注意,SELECT * 表示“选择所有列”。
SELECT * FROM Employees
SELECT 语句中的 WHERE 子句提供了选择值的条件。例如,在以下代码片段中,只有在包含列 Last_Name 的行中出现字符串 'Washington' 时才会选择值。
SELECT First_Name, Last_Name FROM Employees WHERE Last_Name LIKE 'Washington%'
关键字 LIKE 用于比较字符串,并提供了可以使用通配符的功能。例如,在上面的代码片段中,'Washington' 的末尾有一个百分号(%),表示任何包含字符串 'Washington' 加上零个或多个其他字符的值都将满足此选择条件。因此,'Washington' 或 'Washingtonian' 都会匹配,但 'Washing' 不会。LIKE 子句中使用的另一个通配符是下划线(_),它代表任意一个字符。例如:
WHERE Last_Name LIKE 'Ba_man'
将匹配 'Barman'、'Badman'、'Balman'、'Bagman'、'Bamman' 等。
下面的代码片段中的 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 子句涉及连接,这将在下一节中解释。
关系数据库的一个显著特点是可以从多个表中获取数据,这称为连接。假设在检索拥有公司车辆的员工姓名后,想要找出谁拥有哪辆车,包括车辆的制造商、型号和年份。这些信息存储在另一个表 Cars 中:
汽车编号 |
制造商 |
型号 |
年份 |
---|---|---|---|
5 | 本田 | 思域 DX | 1996 |
12 | 丰田 | 卡罗拉 | 1999 |
这两个表必须有一个相同的列,以便将它们关联起来。这个列在一个表中必须是主键,在另一个表中则称为外键。在这个例子中,出现在两个表中的列是 汽车编号
,它是表 汽车
的主键,也是表 员工
的外键。如果1996年的本田思域在 汽车
表中被撞毁并删除,那么 汽车编号
为5的员工也必须从 员工
表中删除,以保持所谓的引用完整性。否则,员工
表中的外键列(汽车编号
)将包含一个在 汽车
表中没有对应值的条目。外键必须为空,或等于所引用表的现有主键值。这与主键不同,主键不允许为空。在 员工
表的 汽车编号
列中有几个空值,因为员工可能没有公司车。
下面的代码要求输入有公司车辆的员工的名字和姓氏,以及这些车辆的制造商、型号和年份。请注意,FROM
子句列出了 员工
和 汽车
两个表,因为请求的数据包含在这两个表中。使用表名和点号(.
)之前的列名表示该列位于哪个表中。
SELECT Employees.First_Name, Employees.Last_Name, Cars.Make, Cars.Model, Cars.Year FROM Employees, Cars WHERE Employees.Car_Number = Cars.Car_Number
这将返回一个类似以下的结果集:
名字 |
姓氏 |
车牌号 |
里程数 |
年份 |
---|---|---|---|---|
约翰 | 华盛顿 | ABC123 | 5000 | 1996 |
弗洛伦斯 | 沃约科夫斯基 | DEF123 | 7500 | 1999 |
SQL命令分为多个类别,其中最主要的是数据操作语言(DML)命令和数据定义语言(DDL)命令。DML命令处理数据,可以检索数据或修改数据以保持其最新状态。DDL命令用于创建或更改表和其他数据库对象,例如视图和索引。
以下是常见的DML命令列表:
SELECT —
用于从数据库查询和显示数据。 SELECT
语句指定了要在结果集中包含哪些列。应用程序中使用的大多数SQL命令都是SELECT
语句。
INSERT —
向表中添加新行。 INSERT
用于填充新创建的表或向已存在的表中添加新行(或多行)。
DELETE —
从表中删除指定的行或一组行
UPDATE —
更改表中的现有列或一组列中的值
以下是常见的DDL命令:
CREATE TABLE —
根据用户提供的列名创建表。用户还需要为每个列中的数据指定类型。数据类型因不同的关系数据库管理系统而异,因此用户可能需要使用元数据来确定特定数据库使用的数据类型。与数据操作命令相比,CREATE TABLE
通常使用较少,因为表只会创建一次,而添加或删除行或更改单个值通常会更频繁发生。
DROP TABLE —
删除所有行并从数据库中删除表定义。根据SQL92的过渡级别规定,JDBC API实现需要支持DROP TABLE命令。然而,对于DROP TABLE的CASCADE和RESTRICT选项的支持是可选的。此外,当存在引用被删除的表的视图或完整性约束时,DROP TABLE的行为是实现定义的。
ALTER TABLE —
向表中添加或删除列。还可以添加或删除表约束并修改列属性
满足查询条件的行称为结果集。结果集返回的行数可以是零、一或多。用户可以逐行访问结果集中的数据,游标提供了这种功能。可以将游标视为指向包含结果集行的文件的指针,并且该指针具有跟踪当前访问的行的能力。游标允许用户从顶部到底部处理结果集的每一行,因此可用于迭代处理。大多数DBMS在生成结果集时会自动创建一个游标。
早期的JDBC API版本为结果集的游标增加了新的功能,使其能够向前和向后移动,还允许它移动到指定的行或相对于另一行的位置。
更多信息请参见从结果集中检索和修改值。
当一个用户正在访问数据库中的数据时,另一个用户可能同时访问相同的数据。例如,如果第一个用户在同时更新表中的某些列,而第二个用户在同时从同一张表中选择列,那么第二个用户可能会得到部分旧数据和部分更新数据。因此,DBMS使用事务来保持数据处于一致状态(数据一致性),同时允许多个用户同时访问数据库(数据并发性)。
事务是由一个或多个SQL语句组成的逻辑工作单元。事务以提交(commit)或回滚(rollback)结束,具体取决于数据一致性或数据并发性是否存在问题。提交语句会永久保存事务中SQL语句导致的更改,而回滚语句会撤销事务中SQL语句导致的所有更改。
锁是一种机制,禁止两个事务同时操作相同的数据。例如,表锁会阻止在该表上存在未提交的事务时删除表。在某些DBMS中,表锁还会锁定表中的所有行。行锁会阻止两个事务修改相同的行,或者阻止一个事务在另一个事务修改它时选择该行。
更多信息请参见使用事务。
存储过程是一组可以按名称调用的SQL语句。换句话说,它是可执行的代码,是一个执行特定任务的小程序,可以像调用函数或方法一样调用它。传统上,存储过程是用DBMS特定的编程语言编写的。最新一代的数据库产品允许使用Java编程语言和JDBC API编写存储过程。用Java编程语言编写的存储过程在DBMS之间是字节码可移植的。一旦编写了存储过程,它可以被使用和重复使用,因为支持存储过程的DBMS会将其存储在数据库中,正如其名称所暗示的那样。有关编写存储过程的信息,请参见使用存储过程。
数据库存储用户数据,也存储有关数据库本身的信息。大多数DBMS都有一组系统表,列出数据库中的表,每个表中的列名,主键,外键,存储过程等等。每个DBMS都有自己的函数来获取有关表格布局和数据库特性的信息。JDBC提供了接口DatabaseMetaData
,驱动程序编写者必须实现这个接口,以便它的方法返回有关驱动程序和/或DBMS的信息。例如,许多方法返回驱动程序是否支持特定功能。这个接口给用户和工具提供了一种标准化的方式来获取元数据。一般来说,编写工具和驱动程序的开发人员最有可能关注元数据。