|
课程概述JDBCTM入门
JDBCTM 是一种 Java TM (应用编程接口),它制定处理表格数据和常见的关系型数据 的标准框架。当 JDBC 2.0开始让程序员们了解 SQL 时,SQL 仍然是标准数据库 引擎的混合用语,它代表着将数据从代码中分离出来的重大行业成功。在介绍这 门课程本身之前,花点时间了解一些从 SQL 直接演变到 JDBC 的背景知识是值得 的。 SQL
SQL 是用于创建、操作、检查和管理关系型数据库的标准化语言。虽然本课程 提供了“SQL 初步”和“ SQL 资源”,但不会广泛地讲解 SQL。 不过,将介绍下列内容: 数据库本质上是表的“智能”容件。 表是由行组成的容件。 行(概念上)是由列组成的容件。 列是具有名称、类型和值的单个数据项目。 在开始复习此定义并了解其重要区别时,可以采用以下类比:数据库相当于文 件系统;表则相当于文件;行相当于记录或构件;列相当于域或变量。如果还不 熟悉这些术语,那么在继续学习本课程之前要复习一些编程知识,特别是“输入 /输出(I/O)”操作中的知识。 因为 SQL 是应用程序的专用语言,所以一个语句可以具有很多操作含义,可 以对整个数据集启动高级操作(如分类合并)。SQL 标准制定于 1992 年。以便 程序可以和大多数数据库系统通信,而无需更改 SQL 命令。不过,在发送 SQL 命令之前,必须连接到数据库;并且,每个数据库供应商都有不同的发送界面和 不同的 SQL 扩展。进入 ODBC。 ODBC
ODBC(Open Database Connectivity,开放式数据库连接性)是基于 C 语言 的界面,它指向基于 SQL 的数据库引擎,提供了与数据库通信以及访问数据库 元数据(元数据是关于数据库系统供应商与数据存储方式等等的信息)的一致性 界面。个别供应商会给他们特别的数据库管理系统提供特有的驱动程序或“桥路 器”。因此,有了 ODBC 和 SQL,就可以连接到数据库,并按标准方式进行操作。这就难怪尽管开始是 PC 标准的 ODBC ,现在已经几乎变成了行业标准。 虽然 SQL 非常适合于操作数据库,但其设计目的并非常规的应用程序语言; 相反,它是仅作为与数据库通信的工具而设计的。还需要一个更通用和更完整的
编程语言,来安排 SQL 语句和向数据库输入,并处理结果以供数据操作、视觉显 示或报表生成。令人遗憾的是,无法顺利地编写可以在多平台上运行的程序,即
使数据库连接性的标准化问题已经大部分得到了解决。例如,在 C++ 中编写数据 库客户程序时,要让它在另一个平台上运行可能必须全部重写客户程序。即,
PC 版软件不能在 Macintosh 上运行。理由有两个。第一, C++ 作为语言不具有 可移植性,因为 C++ 的规定不彻底(例如, JavaTM 编程语 言和 JDBC
编写正确且遵守规范的 Java 程序,可以无需重新编译就在任何启用 Java 技 术的平台上运行。Java 编程语言彻底地进行了规定。根据定义,启用
Java 技术 的平台必须支持已知的核心库。 注意: 虽然可移植的应用程序和标准数据库界面都是重大的成果,但不要 忘记,因为历史、竞争、有时是没有意义的原因,各种数据库并没有彻底地进行 标准化。这可能意味着,必须根据(甚至同一平台上的)特定数据库的性能或内 在的调整来寻找“最低公分母”。无论采用标准的 SQL、 ODBC、 JDBC、或其他 解决方案,都存在这个问题。 JDBC 驱动程序属于类,它实现 JDBC 驱动程序界面,并可以为特别的数据库 转换程序(一般是 SQL)请求。无疑,驱动程序在这里起了重要作用。驱动程序 类型有四种,“ JDBC 驱动程序类型”中的 JDK( Java Development Kit, Java 开发工具 箱)里对此进行了介绍。本课程采用第四类驱动程序,是因为它们有近乎零的安 装要求和富于变化的性质。就特别的项目而言,另一种驱动程序类型可能更有意 义。大多数的数据库供应商现在都提供驱动程序,以实现特定系统的 JDBC API。 这些通常都是免费提供的。第三方驱动程序也可以获得,成本从免费到费用浩大 的都有。到 JDBC 驱动程序资源的链接,请查看“专门信 息”和另一个资源。 JDBC 1.0
JDBC 1.0 API 提供基本的数据访问准则,主要由以下界面和类组成: 驱动程序
连接 语句
类型 如本课程所介绍的,要将“驱动程序”传递到 JDBC 2.0
JDBC 2.0 API分为两个部分:即本课程将介绍的核心 API 和“ JDBC 2.0 可选包”。一般说来,JDBC 2.0 核心 API 添加了一些类,但主要 涉及性能、类增强和功能以及新的 SQL3(又名 SQL - 99)数据类型。 核心 API 中的新功能包括可卷动的结果集、批更新、编程性插入、删除、更 新、性能提示、国际化 Unicode 字符流的字符流、完全精确的
当准备本课程的时候,JDBC 3.0草案正在考虑中,预计会出现在 JDK 的 1.4 版本中。 完整范例
本课程中的第一个 JDBC 实践经验是基本而完整的范例。该范例说明了有关创 建并访问数据库信息的全面概念。编写数据库应用程序时遇到的基本问题是: 创建数据库。数据库可以使用数据库供应商提供的工具创 建;或经由从 Java 程序提供给数据库的
SQL 语句创建。因为通常会有数据库管 理员(当然,可能是开发者您)负责,且并非全部的 JDBC 驱动程序都要通过 “数据定义语言”(
Data Definition Language, DDL)来 支持数据库的建立,所以,本文大体上会专门介绍
DBMS ( DataBase Management System,数据库管理系统)和驱动程序。如果您对进一步的细节感兴 趣,可以参看典型的“创建数据库”语句;但务必复习
DBMS SQL参考,尽管它不 是 SQL 标准的构成,但却是由 DBMS 决定的。 连接数据库这是 JDBC 驱动程序最重要的工作,且必须向它传递特定信息。要 求的基本信息有“数据库 URL(统一资源定位符)”、“用户标识”和“密码”。 根据驱动程序的不同,可能还有很多其他的参数、属性或特性。这里有两个范例:
创建表。虽然数据库包含有表,但这些表都是包含数据的 实际的组件,形式是行列表单。表的创建由DDL
CREATE TABLE 语句完 成。本语句有多种选项,各个供应商有些不同。再次强调遇到特殊情况时务必复 习 DBMS
SQL 参考资料。 在数据库中插入信息。可以使用数据库的特殊工具添加和 维护数据,或用 SQL 语句以编程的方式发送出去。根据设计,本课程将集中介绍
通过 JDBC 将 SQL 语句发送到数据库。 有选择地检索信息。在发送 SQL 命令去检索数据,并使 用 JDBC 将结果变成变量后,程序代码就和任何其他变量一起显示或操作该数据。 方案描述
本范例的初始任务要求安装构件并插入数据,以跟踪 jGuru Jive Java Jumphouse 上的 Java (即咖啡)进入量,简称“ 4J 咖啡”。 然后,必须生成“ 4J 咖啡”的管理报告,包括咖啡总销售额以及顾客一天内消 费的最大咖啡量。数据如下: “在 4J 咖啡中,咖啡因是我们最重要的产品”
创建数据库
如前所述,数据库的创建是特定于 DBMS 的。为有助于了解范例,这里对遵 守 JDBC 标准的基本过程规则进行了例外处理。只需设置传递到驱动程序的数据
库连接的 URL 属性,就可以在 Cloudscape 中创建数据库。该属性的设置是: 注意: 记住,这是 Cloudscape 的方法,不一定适用于任何其他的 DBMS。 例如,在 UDB2/NT 上创建数据库,就要使用
连接数据库
使用 装载 JDBC 驱动程序。 必须装载启用 JDBC 类的驱动程序,以和数据源通信。在初始范例中,与 Cloudscape 一起使用的驱动程序类,
Class.forName( DriverClassName); 标准的 JDBC CompliantTM驱动 程序也用本代码创建新的驱动程序类。令人遗憾的是,实际上不是所有情况都行 得通。因此,本练习使用以下代码: Class.forName(DriverClassName).newInstance(); 尽管多数情况下,本代码会创建额外的对象,但用于确定是否创建了对象实例 并在没有创建时创建新对象所要求的代码,通常超出新建的成本。好在废料收集
器最后会清理未引用的对象,而 借助于 java -Djdbc.drivers=DriverClassName AJavaApp 本课程中使用的连接到 Cloudscape 的特定 DriverClassName 的建议创建方 式是: COM.cloudscape.core.RmiJdbcDriver 连接数据源。 驱动程序提供创建“连接”的方法,但要求使用 jdbc 协议的特定的 URL 类 型。通用的表单是 jdbc:<subprotocol>:<subname>。更 多信息,请查看“ JDBC API 基础知识”中的“ 常规使用和 JDBC URL”。 人们经常认为理当如此的一个明显的观点是:使用 URL 意味着 JDBC 应用程 序会自动 jdbc:cloudscape:rmi:jGuru;create=true 在使用 Connection con = DriverManager.getConnection( URL, Username, Password ); 本形式具有最佳的可移植性,即使“用户名”和“密码”由于数据库默认或 ODBC 数据源的文本文件无法使用此类属性而为空字符串(
就 Cloudscape 驱动程序而言,这是因‘create = true’URL 属性(随后连 接将去掉)创建数据库的实际所在。 创建表
虽然“连接”类有许多性能,但是为了使用 DDL 或“数据操作语言”(Data Manipulation Language,DML)SQL 语句,仍要求“语句”对象。所以,下一步 是向“连接”要求“语句”对象: Statement stmt = con.createStatement();此时,程序可以开始起一点实际作用了。为了存储数据,范例在 jGuru 数据库中 创建了命名为 JJJJData
的表。下面是该 SQL 语句,包括每个数 据项需要的列。为清晰起见,范例中的 SQL 关键字都是大写,但这根据程序员的 爱好而定,并非必需如此。
CREATE TABLE JJJJData (
Entry INTEGER NOT NULL,
Customer VARCHAR (20) NOT NULL,
DOW VARCHAR (3) NOT NULL,
Cups INTEGER NOT NULL,
Type VARCHAR (10) NOT NULL,
PRIMARY KEY( Entry )
)
其程序代码是:
stmt.executeUpdate( "CREATE TABLE JJJJData (" +
"Entry INTEGER NOT NULL, " +
"Customer VARCHAR (20) NOT NULL, " +
"DOW VARCHAR (3) NOT NULL, " +
"Cups INTEGER NOT NULL, " +
"Type VARCHAR (10) NOT NULL," +
"PRIMARY KEY( Entry )" +
")" );
注意,实际的 SQL语句没有终结符。不同的数据库使用不同的终结符,代码列表 中为了可移植性没有使用终结符。相反,将插入适当终结符的任务交给了驱动程
序。
该代码还对数据库表明,没有“零值”列,这主要是为了避免给 SQL 新手带 来困扰。为了识别每行,代码定义了主键。 在数据库中插入信息
表创建后,就可以使用 SQL 的 INSERT 语句添加数据了: INSERT INTO JJJJData VALUES ( 1, 'John', 'Mon', 1, 'JustJoe' ) INSERT INTO JJJJData VALUES ( 2, 'JS', 'Mon', 1, 'Cappuccino' ) INSERT INTO JJJJData VALUES ( 3, 'Marie', 'Mon', 2, 'CaffeMocha' ) ...在程序范例中,命名为 SQLData的阵列包含有实测值,每个元素的 形式类似于:
"(1, 'John', 'Mon', 1, 'JustJoe')"The program code corresponding to the INSERT statements
above is:
stmt.executeUpdate( "INSERT INTO JJJJData VALUES " + SQLData[i] ); 循序渐进
简短地复习一下迄今为止学习的内容:首先,任何 JDBC 程序会装载 JDBC 驱 动程序,并使用 本节的练习包括创建 练习检索数据库中的信息
要从数据库检索信息,可借助于 例如,如何获得 4J Cafe 顾客一天内消费的最大咖啡数量(杯)。根据 SQL, 获取最大值的方法之一是使用“订购者”从句按“杯”列对表进行降序排序。返
回的 SELECT Entry, Customer, DOW, Cups, Type FROM JJJJData ORDER BY Cups DESC在程序中,用下列方式执行 SQL 语句: ResultSet result = stmt.executeQuery(
"SELECT Entry, Customer, DOW, Cups, Type " +
"FROM JJJJData " +
"ORDER BY Cups DESC");
数据导航
如果存在下一行, if( result.next() )if 语句收集数据。然后,采用 while(result.next())循环,以允许程序持续到数据的末尾。 Data Extraction
数据提取一旦定位在行上,应用程序就可以使用适当的 iEntry = result.getInt("Entry");
Customer = result.getString("Customer");
DOW = result.getString("DOW");
Cups = result.getInt("Cups");
TotalCups += Cups; // increment total
Type = result.getString("Type");
程序对 如果运行顺利,会显示下列结果: JS consumed the most coffee, 9 Espressos on Friday! The total cups of coffee consumed was 48. The row by row output is:
注意, 本节的练习包括检查 练习本节结束时,要记住: JDBC 具有可移植性。 为简单起见,此处的驱动程序名称、URL、用户和密码已经进行了固定编码。 用变量替代此信息后,这些程序将以任何 JDBC Compliant 驱动程序运行。 本节所有代码和材料适用于并运行于带有适当驱动程序的 JDK 1.1 和 JDBC 1.2。 不过,在这一点上,本课程假定可以使用 JDK 1.3 和 JDBC 2.0(但大多数 材料在 JDK 1.2 之下也可以顺利运行)。 连接 Java 程序和数据库
“ 连接”对象代表并控制到数据库的连接。“连接数据 库”中已经介绍了连接的基本知识;本节将澄清几个要点,介绍“连接”控制 的不同区域,并给出两个练习来示范提供顺利连接所需信息的一般方法。 虽然 JDBC 中的一切都取决于数据库和 JDBC 驱动程序的功能,但一般说来, 可以有多个方法连接到相同的数据库和/或“连接”到多个数据库。DriverManager 类处理驱动程序注册,并提供获取“连接”的方法。注意,全部 DriverManager 方法都是静态的;此处不举例说明。 获取“连接”的第一步常常最难:即,如何创建@_#_$!!!@_#_ database URL? 如上所述,在用<subprotocol>:识别机器或服务器以及<subname>基 本上用来识别数据库时,基本代码jdbc:<subprotocol>:<subname> 显得非常精炼。实际上,其内容取决于专用驱动程序,并会因为产生“没有合适 的驱动程序”的错误等类途径问题使人不知所措,倍受困扰。以上述的范例中使 用的 Cloudscape URL 为例: jdbc:cloudscape:rmi:jGuru上述代码会解译成 jdbc: <subprotocol>: <subname> jdbc: cloudscape:rmi: jGuru这个相当简单,主要是因为客户程序且服务器都在相同的机器上运行。在低于第 四种类型的驱动程序中常常见到类似的 URL,因为要涉及到另外的设置,且定位 服务器要求的信息要从设置信息处获得。 即使在这里,情况并不总是令人满意。支持远程(并且甚至当地)连接的大多 数 DBMS 引擎,都使用TCP / IP(传输控制协议/
Internet协议)端口来做到 这一点。事实上,即使 Cloudscape 也在启动后使用 在应用程序试着连接网络或 Internet 服务器时,必须提供识别/存储单元信 息。常见的 JDBC 方法是使用 jdbc:cloudscape:rmi:jGuru;create=true;create=true部分是使用 Cloudscape 句法的属性。含义是:在文档 中,检查出驱动程序和数据库。 “连接”在收集无用数据时自动关闭,但审慎的程序员会始终明确地关闭“连 接”以直接地确保节省资源。注意,虽然 API 会明确地表示关闭“连接”以“立 即释放数据库和 JDBC 资源”,但 JDBC 会建议关闭“连接”和“语句”。 和其他 JDBC API 的重要区域一样,“连接”也是界面。很多的程序员想 知道对象的起源处,因为界面不能用具体事例来予以说明。扼要的答案是: JDBC 驱动程序实现了界面并在请求时返回实际对象。 这同时说明了为什么应用程序编 译时十分完整,运行时却问题成堆:因为代码是参照标准接口编译的,只有装载 并运行程序和驱动程序时才能得到实际的效果。 “连接”界面控制的区域
前面的大多数章节都介绍了 创建“语句”、 获取 通过 设置事务涉及的隔离级别。 在给定数据库的本地非标准语言中,甚至有获取任何 SQL 语句的方法,该方 法的适当名称是 在继续介绍前,先讲解“
JDBC 2.0 可选包”引进的新 DataSource
类。该规范说明建议将 连接信息概述
从上面获取“连接”对象所需信息的介绍中可知,对信息进行固定编码不是个 令人满意的决定。以下练习给出了在两个通用的编程方案中获取该信息的两种方
法---即使用
您可能想知道,练习中给登录名和密码设置的“sa”和“admin”是 Cloudscape 的默认值还是胡乱设置的。答案是,在逻辑框以外,没有启用 Cloudscape 的验证/安全。必须亲自对它进行设置。否则,它就会忽视这些无效 参数和属性。从一开始,就已经引入了这些有效的虚拟名称,以介绍 JDBC 标准 “连接”参数。这再次强调了检查驱动程序和数据库文档的重要性。第二个答案 是,如同在很多其他区域中一样,编程时可能有镜像,但没有什么魔法。 练习语句、ResultSets 以及与数据库的互动
“语 句”对象是发送/执行(常规) SQL语句,并通过相关“连接”检索结果的容 件或传输机制。“连接界面控制的区域”中曾经介 绍,语句类型有三种,包括 Prepared Statements 和 Callable Statements,两者都是“语句”的子界面。 如前所述,无需创建新的“语句”实例,而是请求相关的“连接”进行创建: Statement stmt = con.createStatement(); execute 系列是“语句”方法最常使用的: executeQuery() 用来执行返回单个 ResultSet 的 SQL 语句。 executeUpdate()用来执行修改表或表中列值的 SQL 语句,并返回 修改过的行数(在 DDL 语句中为零)。 execute() 可用于执行任何类型的 SQL语句,但更针对那些可以返 回多个结果或值的 SQL语句。本课程不深入介绍该语句。 为了更灵活地使用不同的数据库和数据源, JDBC 没有限制语句可以发送的 SQL 语句种类。实际上,只要数据源可以识别(这是程序员的责任了),语句甚 至无需是 SQL 语句,这就带来了一些令人感兴趣的可能性。不过,标明为 JDBC Compliant 的驱动程序必须至少支持 ANSI SQL-92 Entry Level 的性能。 在“连接”收集无用数据时,语句将自动地关闭,但在不再需要时应该亲自关 闭。JDBC 建议始终明确地关闭该“语句”。 修改数据
更新对程序员来说有特别的含义,甚至对 SQL 也是如此。所以,对于用来执 行 DML( JDBC 对类型进行定义以匹配 SQL 数据类型。这些定义必须适合于数据,以避 免出现技术问题、意外结果,并能促进工作效率。可用和合适类型的更详尽信息, 请查看“Java- SQL 类型等效性”。
练习数据库查询
int iCount = 0;
while( myResultSet.next() )
{
// retrieve column data
// do something with it
iCount++;
}
if( iCount == 0 )
{
System.out.println(
"myResultSet returned no data.");
}
else
if( bNoErrorsOrExceptionsOrEarlyTerminations )
{
System.out.println(
"All rows from myResultSet were processed.");
}
应该按从左到右的语句(次序和
“语句”一次仅仅打开一个
练习筹备语句
已包含的 SQL 会被发送到数据库,并预先进行编译或筹备。由此开始发送已 筹备的 SQL,本步骤会被省略。更有活力的“语句”对每个执行都要求本步骤。
根据 DB 引擎的不同,SQL 可能会进行高速缓存并重复使用。即使对不同的
例如, 注意: SQL3 类型通常假设使用 DML 的筹备语句。 这里,有两个安装和获取筹备语句的范例: pstmtU = con.prepareStatement(
"UPDATE myTable SET myStringColumn = ? " +
"WHERE myIntColumn = ?" );
pstmtQ = con.prepareStatement(
"SELECT myStringColumn FROM myTable " +
"WHERE myIntColumn = ? ");
问号,也被称作参数标志,是在语句执行前待设置的值。从 1 开始,按从左 到右的数字顺序对它们进行引用。 pstmtU.setString( 1, "myString" ); pstmtU.setInt( 2, 1024 ); pstmtU.executeUpdate(); pstmtQ.setInt( 1, 1024 ); pstmtQ.executeQuery(); 也可以筹备没有参数的语句。注意, 练习Java - SQL类型等效性
JDBC 的"
类型"定义为转换成标准 Java 类型,提供了属类的 SQL 类型。通常是直接 确定所需的类型和方法。以下两个表显示用于获取每个数据类型的常规
为了显示,
这些看起来非常清晰明了,难度也不大,但专业程序设计员应该花些时间阅读 映射 Java 的 SQL 数据类型和映射 SQL 及 Java "类型"。尤其要通过" ResultSet.getXXX() 方法"检查"转换"表,以查看可用选项的种类。 对于应用定位程序的 SQL3 类型,因为文档上令 人遗憾的缺陷,人们常常对它发出这样的疑问:"开始时,该如何将类型输入数据
库呢?"最好的答案是,检查它们对应的类(例如, JDBC 例外类型以及例外处理
"我再不愿意想这件事情。"---一般而言,实话实说的开发者对问到例外/错误 处理时的反应很可能就是这句话。它说明了很难正确地进行处理,还常常会吃力 不讨好。这对于编制优质的应用程序也很关键。 本课程中的练习突出强调专门的 JDBC 区域,未对生产质量提出要求。同时, 从第一个练习开始介绍了异常处理的标准。不过,标准并不完整,因此介绍了三
种 注意,加入 JDBC 2.0 中的第四种类型, SQL 例外
这些方法应该相当直接了当。典型的捕捉代码看起来如下所示: try
{
// some DB work
} // end try
catch ( SQLException SQLe)
{
while( SQLe != null)
{
// do handling
SQLe = SQLe.getNextException();
}
} // end catch
提示: 程序员常常被语法错误弄得迷惑不解,这些语法错误似乎参 考一些无形的操作,如"ungrok found at line 1, position 14."不断地报告异 常处理程序中Connection.nativeSQL(yourQueryString)的输出量将会澄清事 实。 SQL 警告
"语句"在进行下一个执行时自动地清除警告。 获取 try
{
...
stmt = con.createStatement();
sqlw = con.getWarnings();
while( sqlw != null)
{
// handleSQLWarnings
sqlw = sqlw.getNextWarning();
}
con.clearWarnings();
stmt.executeUpdate( sUpdate );
sqlw = stmt.getWarnings();
while( sqlw != null)
{
// handleSQLWarnings
sqlw = sqlw.getNextWarning();
}
} // end try
catch ( SQLException SQLe)
{
...
} // end catch
数据截断
数据截断主要表示读取或写入的信息要比请求的少。一些数据库/驱动程序会 接受超过列容量的数据,它们会将数据截断,再写入已截断数据,并通过
DataTruncation 类包括下列与截断数据信息有关的方法: 样本误差测试结果
下面的是一组从 Cloudscape,UDB2/NT 和 DB2/400处返回的、特定地用于提 示问题的实际错误信息。详情请查看本节 练习。
<
练习元数据
元数据是关于数据的数据(或信息)。JDBC 允许程序员通过元数据类去发现 关于数据库和任何给定 数据库元数据
为了发现数据库的信息,必须获取 DatabaseMetaData dbmd = con.getMetaData(); 好在只调用所希望信息的方法,就可以做到这一点。大部分的问题是:
许多方法返回 部分方法,包括返回数据库和表组件的信息,使用的名称格式十分混乱。根据 数据库的不同,信息可能采取大写字母、小写字母或大小写混合的形式,格式是 区分大小写的。结果,必须调用方法在试图获取信息前,找出信息存储的方式。 虽然这可能有些不令人满意,但可以轻松获得最常用的 对本课程中的程序已提供了链接,这些程序使用下面"练习"部分的 练习ResultSet 元数据
为了发现给定 ResultSetMetaData rsmd = rs.getMetaData();
对本课程中的程序已提供了链接,这些程序使用下面"练习"部分的 练习扩展符句法和标量函数
大多数的数据库提供标量函数(有时称为内部函数),用于执行列的特定值上 的操作,甚至提供迅速创建列的值。JDBC 规范支持不同的数学值、字符串、系统、 时间和日期、X/Open Call Level Interface(打开调用级别界面,CLL)指定的 类型转换功能;如果底层的 DBMS 支持某功能,则 JDBC Compliant 驱动程序也 必须支持。这些功能的名称应该和 X/Open 名称匹配,虽然情况不总是这样。对 其功能或从应用程序切换到数据库而言,标量函数非常重要。 JDBC 提供这些方法,以确定标量函数: 因为不同的数据库对标量函数调用使用不同的语法,所以 JDBC 定义了专门的 扩展符语法。JDBC驱动程序会理解这些语法,并映射到适当的语法以找出基础数
据库。扩展符也用于 在 SQL 语句中,标量函数通常和列一起使用。例如,
or
受到支持的功能,请查阅 DBMS 手册。 练习存储过程
存储过程是用户生成的函数或步骤,在注册数据库后,可以称为客户应用程 序。这些过程很有价值,因为它们可以将工作切换到服务器,并减少编码,对于 复杂的操作尤其如此。令人遗憾的是,对于创建存储过程而言,没有标准的方式、 要求甚至语言。而且并非全部的数据库都支持它们。在这种情况下,就没有办法 创建常用的练习,所以本节只限于讨论使用 JDBC 标准方法调用存储过程及其代 码片断。当然,创建存储过程可以一次性操作成功,并提示其名称和要求的参数 类型。 元数据支持
有几种
参数详情
在调用时,如同标准方法或功能一样,存储过程可以接收零、更多自变数或参 数,也称为 IN 参数。它们可以返回
扩展符句法x
"扩展符语法和标量函数"中曾经提到,存储过程要 求
JDBC 扩展符语法以进行标准调用。驱动程序会再次处理实际映射。基本格式 的组成是: A - 不带参数,它返回 { call sp_A }
B - 单一参数,返回结果参数。假设为 int 结果参数和 String IN
参数: { ? = call sp_B( ? ) }
C - 多参数,它返回 ResultSet 或行数。假设为 int IN、
OUT 和 INOUT 参数: { call sp_C( ? ? ? ) }
CallableStatement
要向数据库发送实际的存储过程执行请求,可使用 A - CallableStatement cstmt =
con.prepareCall( "{ call sp_A }" );
B -
CallableStatement cstmt =
con.prepareCall( "{ ? = call sp_B( ? ) }" );
C -
CallableStatement cstmt =
con.prepareCall( "{ call sp_C( ? ? ? ) }" );
设置、调用和值检索
在调用存储过程前,参数标志必须与变量和类型匹配。类型信息,请查看 "Java - SQL 类型等效性"。 IN 参数用从 必须使用 必须设置和注册 INOUT 参数。 实际的调用将根据预期结果照例采用 A - CallableStatement cstmt =
con.prepareCall( "{ call sp_A }" );
就无返回值而言:
cstmt.execute(); // could use executeUpdate()就已返回的 ResultSet 而言:
ResultSet rs = cstmt.executeQuery();就已返回的更新计数而言: int iUC = cstmt.executeUpdate(); B - CallableStatement cstmt =
con.prepareCall( "{ ? = call sp_B( ? ) }" );
// int result parameter cstmt.registerOutParameter( 1, Types.INTEGER ); // String IN parameter cstmt.setString( 2, "M-O-O-N" ); cstmt.execute(); // could use executeUpdate() int iRP = cstmt.getInt( 1 ); C - CallableStatement cstmt =
con.prepareCall( "{ call sp_C( ? ? ? ) }" );
设置:
// set int IN parameter cstmt.setInt( 1, 333 ); // register int OUT parameter cstmt.registerOutParameter( 2, Types.INTEGER ); // set int INOUT parameter cstmt.setInt( 3, 666 ); // register int INOUT parameter cstmt.registerOutParameter( 3, Types.INTEGER );就无返回而言 ( OUT 和 INOUT 除了:) cstmt.execute(); // could use executeUpdate() // get int OUT and INOUT int iOUT = cstmt.getInt( 2 ); int iINOUT = cstmt.getInt( 3 );就已返回的 ResultSet而言:
ResultSet rs = cstmt.executeQuery(); // get int OUT and INOUT int iOUT = cstmt.getInt( 2 ); int iINOUT = cstmt.getInt( 3 );就已返回的更新计数而言: int iUC = cstmt.executeUpdate(); // get int OUT and INOUT int iOUT = cstmt.getInt( 2 ); int iINOUT = cstmt.getInt( 3 ); 所有这些都是很细致的工作,但格式应该明确。 事务
在SQL术语中,事务是逻辑工作单元(logical unit of work,LUW)构成的一 个或多个语句。这在某种含义上意味着,一切都是事务。不过,通常而言,术语 事务用来表示或全或无的系列操作;也就是说,要么一切十分成功,要么什么也 没有发生。 典型的事务是从银行帐户提款,并存放到另一个。只要提款完成,金额就消失 了。另一个范例是复式簿记记帐法中的借方和贷方:借方和贷方都必须完成。第
三个方面,即本节练习中的内容,是保证 虽然一些 SQL非标准语言有专门的开始和结束事务语句,但总的来说事务会从 程序开始持续进行到语句联锁。并从该点,开始新的事务。这是 JDBC 所使用的 模型。JDBC 驱动程序的默认值是 autocommit,表示每个 SQL 语句的 结果一旦执行就永久保留。这是本课程到现在为止无需考虑事务的原因,而且在 很多情况下都很能让人接受。 注意: 在 autocommit 模式中,联锁出现于"语句"完成时。"语句" 返回
"连接"的 联锁
一旦 autocommit 设置为 false,全部数据库 DML 语句在联锁前都可以视为 暂时性语句。JDBC
支持带 注意: 事务中的 DDL 语句可以忽视或形成联锁。该特性是由 DBMS 决定的,
并可以通过 回退
并行
大多数的 DBMS 允许多个用户同时对数据进行操作。有时,开发者不太关心数 据库并行问题。(在数据开始消失或数据库数据库发生异常时,开发者常常会处 于冒险状态,至少也会同主管争执。) 并行的级别和类型对性能也有影响。 JDBC 识别下列控制并行性的事务 隔离级别。 TRANSACTION_NONE TRANSACTION_READ_COMMITTED TRANSACTION_READ_UNCOMMITTED TRANSACTION_REPEATABLE_READ TRANSACTION_SERIALIZABLE 使用 在数据库操作中适当而有效的并行处理极端重要,很多应用程序根本无法达到 要求。令人遗憾的是,全面讨论几乎要占很大的篇幅,所以请参看 DBMS 供应商 信息和"资源"以了解更多信息。 典型事务代码
下面是一个典型事务处理代码的范例: con.setAutoCommit( false );
...
bError = false;
try
{
for( ... )
{
// validate data, set bError true if error
if( bError )
{
break;
}
stmt.executeUpdate( ... );
}
if( bError )
{
con.rollback();
}
else
{
con.commit();
}
} // end try
catch ( SQLException SQLe)
{
con.rollback();
...
} // end catch
catch ( Exception e)
{
con.rollback();
...
} // end catch
练习批更新装置
"批更新装置"是 JDBC 2.0 的新功能,允许以多个语句为单位向数据库进行发 送,从而提高性能。注意,实现该功能不需要驱动程序,采用了驱动程序并不比
常规提交更有效率。即使如此,使用批更新无需额外努力(报告除外),且潜在 的好处可能很有价值。驱动程序的支持可以使用 JDBC 2.0 语句是使用命令的自动关联列表创建的。 可以在更新计数阵列中返回的 -3--操作错误。驱动程序的拥有可以在第一个错误处停止 的选项,会给出
-2--操作成功,但是作用过的行数未知。 零--DDL 语句或没有受操作影响的行。 大于零--DDL 语句成功,受操作影响的行数。 典型批更新代码
下面是一个典型批更新的范例: try
{
con.setAutoCommit( false );
...
bError = false;
stmt.clearBatch();
// add SQL statements
stmt.addBatch( sUpdate1 );
stmt.addBatch( sUpdate2 );
stmt.addBatch( sUpdate3 );
// execute the statements
aiupdateCounts = stmt.executeBatch();
} // end try
// catch blocks
...
finally
{
// determine operation result
for (int i = 0; i < aiupdateCounts.length; i++)
{
iProcessed = aiupdateCounts[i];
if( iProcessed > 0 ||
iProcessed == -2
)
{
// statement was successful
...
}
else
{
// error on statement
bError = true;
break;
}
} // end for
if( bError )
{
con.rollback();
}
else
{
con.commit();
}
} // end finally
处理 BatchUpdateException
典型 BatchUpdateException处理器
下面是处理批更新例外的范例: catch( BatchUpdateException bue )
{
bError = true;
aiupdateCounts = bue.getUpdateCounts();
SQLException SQLe = bue;
while( SQLe != null)
{
// do exception stuff
SQLe = SQLe.getNextException();
}
} // end BatchUpdateException catch
catch( SQLException SQLe )
{
...
} // end SQLException catch
练习注释: UDB2/NT从 练习可卷动结果集
到目前为止,所有的 stmt = con.createStatement();该方法是 JDBC 1.0 中唯一可用的方法。在 JDBC 2.0 中,存在着允许创建可卷 动和/或可更新的 ResultSet
新方法。
createStatement( int resultSetType, int resultSetConcurrency )resultSetType 可以是 ResultSet.TYPE_FORWARD_ONLY--这是默认值,和 JDBC 1.0 中的一样:仅转
交移动,列一般地仅能读取一次。在 ResultSet.TYPE_SCROLL_INSENSITIVE 允许创建 ResultSet.TYPE_SCROLL_SENSITIVE 允许创建 resultSetConcurrency 可以是 ResultSet.CONCUR_READ_ONLY - 这是默认值,和 JDBC 1.0中的一样: ResultSet.CONCUR_UPDATABLE 允许通过新的 可更新的 注意,已请求的 可卷动的
这些方法的详情请查看 ResultSet。
用法注释
可卷动 可卷动的 所有的行检索时,语句才视为完成。这在
但 练习LOB
JDBC 2.0包括处理若干 SQL3 数据类型的类。本节介绍 LOB 或大对 象。LOB
的定义有两种类型: 从经典关系数据库理论的来看,Clob 属于临界类型---字符很多--- Blob 实 际上根本不是一种类型。全部所知就是,Blob包含一些可以是任何内容的字节数。 显然,这会损害数据独立性的概念。在有其他非常易于接受的方法,使用数据库 跟踪基本上是图形、声频或其他的类型的二进制文件时,尤其如此。注意,没有 机制可以阻止向视为图像的 Blob 中写入声音文件。也没有机制可以去了解原始 源的名称或任何类似的考虑。 定位器
SQL 定位器类型在概念上类似于指针或记录实体的其他信息。JDBC 开发者不 必处理定位器,但有助于理解概念。因为定位器实际上是
JDBC 驱动程序在阵列、 Clob
对于 但是首先如何填充 Blob
对于 首先,
本课程使用的 Cloudscape 版本不支持 SQL3 数据类型。以下练习已对 UDB2/NT 和 DB2/400 进行了测试。 练习SQL一致性
JDBC Compliant 驱动程序的基本要求是,必须支持 ANSI SQL- 92 Entry Level,它基本上是SQL-89
的第2级。以下是不详尽的、基本的 from 从句中的多个表。 数据类型:characterType, decimalType, integerType, smallintType, floatType, realType, doublePrecisionType, 和 numericType. 简单 SQL 表达式:and, or, not, like, =, <>, 算术函数, joins, group bys, having, order by clauses, 和汇总函数 (如sum、count、max、min) 简单的表和列描述符:tableName, columnName. 表描述符中的唯一和主键约束。 检查列描述符中的约束。 支持相关的子查询和 EXIST 子查询。 充分支持功能中的 支持并集。 更多完整数据,包括 SQL- 92 的中级和完全级别详情,请参看: FIPS PUB 127-2: 数据库语言 SQL 的标准。
另外,ODBC 定义的 SQL 语法支持级别可以通过 根据应用程序的不同,可能会返回不支持某些功能的消息,或使用不同的算法 根据 SQL 级别或驱动程序支持的语法类型及其基础 DBMS 提供该功能。 JDBC 2.0 可选包和 J2EE
在 JDBC 2.0 中,实际上有两个包。本课程介绍核心 API 及其功能。第二个 包,被称为 JDBC 2.0 可选包( javax.sql ),包括 DataSource 界面、连接 池、分布式事务及其行集合。Maydene Fisher 编著的文章 JDBC 2.0 可选包概述了额外的功能。 JDBC 和 JDBC 2.0可选包是 Java 2 平台企业版本(J2EE)的组成部分,它包括创建企业-类服务器-端应 用程序所使用的许多技术。 JavaServerTM 页面 (JSPTM)和 JDBC 结合使用
本课程的本节简短地介绍 JavaServer 页基本原理中提到的 JDBC 和 JSPTM 结合使用的各个方面。 本课程集中介绍核心 JDBC 2.0 API。不过,连接池不仅令人满意,也是 JSP 实用的必需技术,并有助于介绍 JDBC 2.0 可选包的一些特性。可以从 JDBC 下载页面获得 JDBC 2.0 可选包二进制文件。DataSource、 ConnectionPoolDataSource 和 PooledConnection 的更多信 息,请查看 Maydene Fisher 编著的 JDBC 2.0 Optional Package 的前 3 部分。 如文中介绍,使用 import javax.naming.*;
import javax.sql.*;
...
Context context = new InitialContext();
DataSource ds = (DataSource)ctx.lookup(
"jdbc/DataSource" );
Connection con = ds.getConnection(
"userID", "password" );
...
finally
{
if( con != null ) { con.close(); }
}
记住, 上述的代码可以这么直接了当,是因为预计可以由数据库管理员使用 DBMS 供 应商提供的工具来创建 上述应用程序代码的 JSP 版本非常类似: <%@ page import="javax.naming.*, javax.sql.*" %>
...
<%
Context context = new InitialContext();
DataSource ds = (DataSource)ctx.lookup(
"jdbc/DataSource" );
Connection con = ds.getConnection(
"userID", "password" );
...
finally
{
if( con != null ) { con.close(); }
}
%>
在显示信息的应用程序中,应该创建粒媒,该粒媒可以处理查询、于请求时可 以返回包含行里的列和/或以单一字符串按照需要报告列的 如果有分页显示,就可以使用可卷动的结果集练习中采用的相对方法,或获取 检索的行数,然后使用绝对行和页面位置。JSP 卷动的更多想法和程序包,请查 看页面调度程序标记库。实现 JSP 的更多想法和信息,请参考 JavaServer 页面 基本原理短训班和资源。 Cloudscape 安装和设置
JavaTM 2 SDK 企业版中的 Cloudscape 数 据库在大多数 JDBCTM 2.0基本原理课程都得 到了应用。本文介绍的版本是 Cloudscape 3.0.4。第一步是获取平台的 J2EETM SDK 和文档,这些可以从 J2EE 主页的下载 & 规范部分找到。请务必阅读并遵行 安装说明书。 如果希望在线阅读文档,请查看 J2EE 文档。Cloudscape 的在线文档,请查看 Cloudscape 3.0.1 文档。 如果下载 J2EE 的唯一目的是将 Cloudscape 用于学习本课程,就不必按安装 说明书中的建议更改 userconfig 脚本。务必执行下列操作步骤: 设置环境变量
设置环境变量
请使用 J2EE 和 J2SE 目录自己的位置和名称。由此开始, 要避免类途径问题,请将下列 jars 从
不必重命名 jars (如果愿意可以使用不同的名称),但是这样做将避免任何 因为所用的常见姓名导致的冲突。可以随意进行各种设置,但是上述步骤不会更 改适当的客户机和服务器操作。注意,该设置会绕过 J2EE,并完全允许使用 Cloudscape。如果想结合本课程的练习使用 J2EE 部分,就必须修改 jGuru 所用的数据库名称或编辑练习程序和资源包,以使用默认的 CloudscapeDB 数据库。 Cloudscape 的更多信息,请查看 J2EE_HOME/doc/cloudscape/index.html 里 Cloudscape DBMS 的下载文档。不是所有文档都适用于 J2EE 下载中包 括的特别版本。因为本课程集中介绍标准,所以不详细讨论数据库细节,并且每 个数据库的设置都不同。这类信息和启动/关闭数据库的详情,请参考自己所用数 据库和驱动程序的文档。不过,会简要地介绍启动和关闭 Cloudscape。 启动和关闭 Cloudscape
确保已经安装 Cloudscape 并按 Cloudscape 安装和 设置中的介绍进行了设置。 必须使用命令行启动 Cloudscape。实际命令(请务必按照需要适当地替换 J2EE_HOME和斜杠或反斜杠)是:
对于正确地关闭 Cloudscape 的数据库完整性来说,尤其重要。除非绝对不可 避免,不要使用 CTRL-C 或其他的终止进程/终止方法。也必须从命令行关闭 Cloudscape。通常,这意味着开启另一个窗/外框,并键入下列命令(同样,请按 照需要适当替换 J2EE_HOME 和斜杠或反斜杠):
Cloudscape 在成功启动和关闭时的输出信息,请查看 http://J2EE_HOME/doc/guides/ejb/html/Tools4.html#11919。 SQL 初步
本节是 SQL 的复习课程,与练习一起使用。它不 SQL 的百科全书式的全部资 源。只介绍 CRUD 操作所需的基本命令。 C--Create R--Read U--Update D--Delete 创建表
创建表时,请使用 CREATE TABLE <table name> (<column element> [, <column element>]...) 列元素的形式是: <column name> <data type> [DEFAULT <expression>] [<column constraint> [, <column constraint>]...] 列约束的形式是: NOT NULL | UNIQUE | PRIMARY KEY 范例: CREATE TABLE java ( version_name varchar (30), major_version int, minor_version int, release_date date); 撤消表时,请使用 DROP TABLE <table name> 访问列
检索一组列时,请使用 SELECT [ALL | DISTINCT] <select list>
FROM <table reference list>
WHERE <search condition list>
[ORDER BY <column designator> [ASC | DESC]
[, <column designator> [ASC | DESC]]...]
T选择列表常常包含由逗号分隔的列的列表或全部选定的"*"。 SELECT version_name, release_date from java; 如果驱动程序支持核心一致性,也可以使用 要执行联合运算以获取多个表的结果, SELECT employee_id, employee_name,
department_table.department_id, department_name
FROM employee_table, department_table
WHERE employee_table.department_id =
department_table.department_id;
也可以指定 from 子句中的用于表的别名,以免出现累赘的名称或少些打字 操作。 SELECT employee_id, employee_name,
d.department_id, department_name
FROM employee_table e, department_table d
WHERE e.department_id =
d.department_id;
存储信息
插入行请使用 INSERT INTO <table name> [(<column name> [, <column name>]...)] VALUES (<expression> [, <expression>]...) INSERT INTO java VALUES
('2.0Beta', 2, 0, 'Aug-1-1997');
如果支持核心语法,就可以使用 更新行请使用 UPDATE <table name>
SET <column name = {<expression> | NULL}
[, <column name = {<expression> | NULL}]...
WHERE <search condition>
删除行请使用 DELETE FROM <table name> WHERE <search condition> 资源
具体信息
在哪里可 以找到 JDBC 驱动程序的综合目录,包括它们支持的数据库? 在哪里可 以找到 SQLException.getSQLState() 可能返回的 SQLStates 列表? 如何设计 servlet/JSP,才能让查询结果像搜索引擎的结果一样在若干页面上显示? 网站
Sun Microsystems, JDBC 主页jGuru's JDBC FAQ 文件编制和说明
Sun Microsystems 处的 Java 技术网站 包括产品和 API。 书JDBC API 教程和参考资料(第二版) 作者Maydene Fisher, Dr. Rick Cattell, Graham Hamilton, Seth White and Mark Hapner (Addison Wesley ISBN 0201433281) 。 用 JDBC 和 Java 进行数据库编程(第二版) 作者 George Reese (O'Reilly & Associates ISBN 1565926161) [ sample chapte] 。 SQL资源
网站
FIPS PUB 127-2: 数据库语言 SQL 的标准。 结构化查询语言入门/p> 书SQL 完全参考 作者James R. Groff and Paul N. Weinberg (McGraw-Hill ISBN 0072118458) Joe Celko 的智者宝典 高级 SQL 编程(第二版)作者Joe Celko (Morgan Kaufmann ISBN 1558605762) 版权 1996 - 2000 jGuru.com 版权所有。
|
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||