JDBC原理

来源:互联网 发布:linux dd u盘 恢复 编辑:程序博客网 时间:2024/04/28 14:56
 
JDBC原理

课程概述

 

JDBCTM 入门

  • SQL
  • ODBC
  • Java 编程语言和 JDBC
  • JDBC 1.0
  • JDBC 2.0

完整范例

  • 方案描述
  • 创建数据库
  • 连接数据库
  • 创建表
  • 在数据库中插入信息
  • 循序渐进
  • 检索数据库中的信息
    • 数据导航
    • 数据提取

连接界面控制的区域

  •  
  • 连接信息概述

语句、ResultSets 以及与数据库的互动

  • 修改数据
  • 数据库查询

筹备语句

Java - SQL 类型等效性

JDBC 例外类型以及例外处理

  • SQL 例外
  • SQL 警告
  • 数据截断
  • 样本误差测试结果

元数据

  • 数据库元数据
  • ResultSet 元数据

扩展符句法和标量函数

存储过程

  • 元数据支持
  • 参数详情
  • 扩展符句法
  • CallableStatement
  • 设置、调用和值检索

事务

  • 联锁
  • 回退
  • 并行
  • 典型事务代码

批更新装置

  • 典型批更新代码
  • 处理BatchUpdateException
    • 可卷动结果集

 

  • 用法注释

LOBs

  • 定位器
  • Clob
  • 团点

SQL一致性

JDBC 2.0 可选包和 J2EE

JavaServer 页面(TM)和 JDBC 结合使用

Cloudscape 安装和设置

启动和关闭 Cloudscape

SQL 初步

  • 创建表
  • 访问栏目
  • 存储信息

资源

  • 具体信息
  • 网站
  • 文件编制和说明
  • SQL 资源
    • 网站

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++ 的规定不彻底(例如,int 保留多少个字 节?)第二,也是更重要的,每个平台支持的库,如网络访问和 GUI(Graphical User Interface,图形用户界面)准则是各不相同的。进入 Java 编程语言和 JDBC。

JavaTM 编程语 言和 JDBC

 

编写正确且遵守规范的 Java 程序,可以无需重新编译就在任何启用 Java 技 术的平台上运行。Java 编程语言彻底地进行了规定。根据定义,启用 Java 技术 的平台必须支持已知的核心库。java.sql 包或 JDBC 就是这样的 一个库,它们可以视为 ODBC 的可移植版本,且其本身就是重大的标准。Java编 程语言和 JDBC 一起使用,可以给编写数据库应用程序提供正确的可移植性解决 方案。

注意: 虽然可移植的应用程序和标准数据库界面都是重大的成果,但不要 忘记,因为历史、竞争、有时是没有意义的原因,各种数据库并没有彻底地进行 标准化。这可能意味着,必须根据(甚至同一平台上的)特定数据库的性能或内 在的调整来寻找“最低公分母”。无论采用标准的 SQL、 ODBC、 JDBC、或其他 解决方案,都存在这个问题。

JDBC 驱动程序属于类,它实现 JDBC 驱动程序界面,并可以为特别的数据库 转换程序(一般是 SQL)请求。无疑,驱动程序在这里起了重要作用。驱动程序 类型有四种,“ JDBC 驱动程序类型”中的 JDK( Java Development Kit, Java 开发工具 箱)里对此进行了介绍。本课程采用第四类驱动程序,是因为它们有近乎零的安 装要求和富于变化的性质。就特别的项目而言,另一种驱动程序类型可能更有意 义。大多数的数据库供应商现在都提供驱动程序,以实现特定系统的 JDBC API。 这些通常都是免费提供的。第三方驱动程序也可以获得,成本从免费到费用浩大 的都有。到 JDBC 驱动程序资源的链接,请查看“专门信 息”和另一个资源。

JDBC 1.0

 

JDBC 1.0 API 提供基本的数据访问准则,主要由以下界面和类组成:

驱动程序

DriverManager

连接

语句

PreparedStatement

CallableStatement

ResultSet

DatabaseMetaData

ResultSetMetaData

类型

 

如本课程所介绍的,要将“驱动程序”传递到 DriverManager, 然后获得“连接”。其次,创建语句、PreparedStatement、或 CallableStatement,并将它们用于更新数据库或执行查询。查询 返回包含有已请求数据的 ResultSet,该 ResultSet 是按“类型”检索的。DatabaseMetaDataResultSetMetaData 类可以用来提供有关数据库或 ResultSet 的信息。

JDBC 2.0

 

JDBC 2.0 API分为两个部分:即本课程将介绍的核心 API 和“ JDBC 2.0 可选包”。一般说来,JDBC 2.0 核心 API 添加了一些类,但主要 涉及性能、类增强和功能以及新的 SQL3(又名 SQL - 99)数据类型。

核心 API 中的新功能包括可卷动的结果集、批更新、编程性插入、删除、更 新、性能提示、国际化 Unicode 字符流的字符流、完全精确的 java.math.BigDecimal 值以及对值为“日期”“时间”以及“时间标示”的时区的支持。

当准备本课程的时候,JDBC 3.0草案正在考虑中,预计会出现在 JDK 的 1.4 版本中。

完整范例

 

本课程中的第一个 JDBC 实践经验是基本而完整的范例。该范例说明了有关创 建并访问数据库信息的全面概念。编写数据库应用程序时遇到的基本问题是:

创建数据库。数据库可以使用数据库供应商提供的工具创 建;或经由从 Java 程序提供给数据库的 SQL 语句创建。因为通常会有数据库管 理员(当然,可能是开发者您)负责,且并非全部的 JDBC 驱动程序都要通过 “数据定义语言”( Data Definition Language, DDL)来 支持数据库的建立,所以,本文大体上会专门介绍 DBMS ( DataBase Management System,数据库管理系统)和驱动程序。如果您对进一步的细节感兴 趣,可以参看典型的“创建数据库”语句;但务必复习 DBMS SQL参考,尽管它不 是 SQL 标准的构成,但却是由 DBMS 决定的。

连接数据库这是 JDBC 驱动程序最重要的工作,且必须向它传递特定信息。要 求的基本信息有“数据库 URL(统一资源定位符)”、“用户标识”和“密码”。 根据驱动程序的不同,可能还有很多其他的参数、属性或特性。这里有两个范例:

*Cloudscape*

数据库连接 URL 属性。

*DB2*

AS/400 TM JDBC 特性。

创建表。虽然数据库包含有表,但这些表都是包含数据的 实际的组件,形式是行列表单。表的创建由DDL CREATE TABLE 语句完 成。本语句有多种选项,各个供应商有些不同。再次强调遇到特殊情况时务必复 习 DBMS SQL 参考资料。

在数据库中插入信息。可以使用数据库的特殊工具添加和 维护数据,或用 SQL 语句以编程的方式发送出去。根据设计,本课程将集中介绍 通过 JDBC 将 SQL 语句发送到数据库。

有选择地检索信息。在发送 SQL 命令去检索数据,并使 用 JDBC 将结果变成变量后,程序代码就和任何其他变量一起显示或操作该数据。

方案描述

 

本范例的初始任务要求安装构件并插入数据,以跟踪 jGuru Jive Java Jumphouse 上的 Java (即咖啡)进入量,简称“ 4J 咖啡”。 然后,必须生成“ 4J 咖啡”的管理报告,包括咖啡总销售额以及顾客一天内消 费的最大咖啡量。数据如下:

jGuru Jive Java Jumphouse 中的咖 啡消费
“在 4J 咖啡中,咖啡因是我们最重要的产品”

 

项目 顾客 DOW 杯 类型 1 John 星期一 1 JustJoe 2 JS 星期一 1 Cappuccino 3 Marie 星期一 2 CaffeMocha 4 Anne 星期二 8 Cappuccino 5 Holley 星期二 2 MoJava 6 jDuke 星期二 3 Cappuccino 7 Marie 星期三 4 Espresso 8 JS 星期三 4 Latte 9 Alex 星期四 3 Cappuccino 10 James 星期四 1 Cappuccino 11 jDuke 星期四 4 JustJoe 12 JS 星期五 9 Espresso 13 John 星期五 3 Cappuccino 14 Beth 星期五 2 Cappuccino 15 jDuke 星期五 1 Latte

创建数据库

 

如前所述,数据库的创建是特定于 DBMS 的。为有助于了解范例,这里对遵 守 JDBC 标准的基本过程规则进行了例外处理。只需设置传递到驱动程序的数据 库连接的 URL 属性,就可以在 Cloudscape 中创建数据库。该属性的设置是: create=true. 在 DBMS 的默认目录中,就创建了已命名的数据库, 此处是 jGuru。对于“Cloudscape 安装和 设置”中介绍的 J2EE 下载,它将为 J2EE_HOME/Cloudscape。 如果数据库已经存在,Cloudscape 会创建“连接”,但另一方面会给出 SQLWarning

注意: 记住,这是 Cloudscape 的方法,不一定适用于任何其他的 DBMS。 例如,在 UDB2/NT 上创建数据库,就要使用 CREATE DATABASE jGuru。 在 DB2/400上,要首先给出 STRSQL 命令,然后使用“CREATE COLLECTION jGuru”。

连接数据库

 

使用 DriverManager 创建数据库连接有两个步骤。

装载 JDBC 驱动程序。

 

必须装载启用 JDBC 类的驱动程序,以和数据源通信。在初始范例中,与 Cloudscape 一起使用的驱动程序类, RmiJdbcDriver,是固定编 码的。动态装载驱动程序的标准方法如下:

Class.forName( DriverClassName);

标准的 JDBC CompliantTM驱动 程序也用本代码创建新的驱动程序类。令人遗憾的是,实际上不是所有情况都行 得通。因此,本练习使用以下代码:

Class.forName(DriverClassName).newInstance();

尽管多数情况下,本代码会创建额外的对象,但用于确定是否创建了对象实例 并在没有创建时创建新对象所要求的代码,通常超出新建的成本。好在废料收集 器最后会清理未引用的对象,而 DriverManager 不会将驱动程序 注册两次。

借助于 jdbc.drivers系统特性,也可以从命令行指定驱动程 序;但此方法要求在编译时间里,驱动程序要在类途径上。

java -Djdbc.drivers=DriverClassName AJavaApp

 

本课程中使用的连接到 Cloudscape 的特定 DriverClassName 的建议创建方 式是:

COM.cloudscape.core.RmiJdbcDriver

 

连接数据源

驱动程序提供创建“连接”的方法,但要求使用 jdbc 协议的特定的 URL 类 型。通用的表单是 jdbc::。更 多信息,请查看“ JDBC API 基础知识”中的“ 常规使用和 JDBC URL”。

人们经常认为理当如此的一个明显的观点是:使用 URL 意味着 JDBC 应用程 序会自动

jdbc:cloudscape:rmi:jGuru;create=true

在使用 DriverManager 类时,如果请求连接已传递 URL 的“连 接”,DriverManager 就会选择适当的驱动程序;这里,仅仅装载 了 Cloudscape 驱动程序。“连接”请求的标准形式如下:

Connection con = DriverManager.getConnection(URL,Username,Password );

 

本形式具有最佳的可移植性,即使“用户名”和“密码”由于数据库默认或 ODBC 数据源的文本文件无法使用此类属性而为空字符串(""), 也是如此。

*Cloudscape*

就 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 驱 动程序,并使用 jdbc 协议(包括此处创建数据库的属性)创建 URL 。此时,程序可以连接到数据库。其次,向返回的“连接”对象请求“语句”。 本节专门范例将使用传递给驱动程序的 SQL 语句创建并填充 JJJJData表。

本节的练习包括创建 JJJJData 表和插入要求行的 完整应用程序的源代码。

练习

 

  1. 创建并填充表

检索数据库中的信息

 

要从数据库检索信息,可借助于 Statement.executeQuery 方法向数据库发送 SQL SELECT 语句,该语句以数据行的形式返回 ResultSet 对象中的要求信息。默认的 ResultSet 会逐行使用 ResultSet.next() (定位到下一行)和 ResultSet.getXXX() 进行检查, 以获得各列的数据。

例如,如何获得 4J Cafe 顾客一天内消费的最大咖啡数量(杯)。根据 SQL, 获取最大值的方法之一是使用“订购者”从句按“杯”列对表进行降序排序。返 回的 ResultSet 中的首行就是最大数量(杯)。全部列都会被选 中,以便程序按预期报告和检验添加到表中的数据。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");

 

数据导航

 

如果存在下一行,ResultSet.next() 会返回布尔值:true;否 则返回 false(表示已经到达数据/集合的末尾)。 原则上,获得 ResultSet 时,指针或光标刚好定位在第一行之前。调用 next() 会移到第一 行,然后是第二行等等。为了获取第一行,即杯数最大的行,可进行一些特别处 理:

if( result.next() )
if 语句收集数据。然后,采用
while(result.next())
循环,以允许程序持续到数据的末尾。

 

Data Extraction

 

数据提取一旦定位在行上,应用程序就可以使用适当的 ResultSet.getXXX 方法逐列获取数据。下面是范例中用于收集数据的方法,以及为每行汇总杯列的 代码。

          iEntry = result.getInt("Entry");          Customer = result.getString("Customer");          DOW = result.getString("DOW");          Cups = result.getInt("Cups");          TotalCups += Cups;  // increment total          Type = result.getString("Type");

 

程序对 System.out.println() 使用报告标准。

如果运行顺利,会显示下列结果:

JS consumed the most coffee, 9 Espressos on Friday!

The total cups of coffee consumed was 48.

The row by row output is:

12 JS Fri 9 Espresso 4 Anne Tue 8 Cappuccino 11 jDuke Thu 4 JustJoe 8 JS Wed 4 Latte 7 Marie Wed 4 Espresso 13 John Fri 3 Cappuccino 9 Alex Thu 3 Cappuccino 6 jDuke Tue 3 Cappuccino 14 Beth Fri 2 Cappuccino 5 Holley Tue 2 MoJava 3 Marie Mon 2 CaffeMocha 15 jDuke Fri 1 Latte 10 James Thu 1 Cappuccino 2 JS Mon 1 Cappuccino 1 John Mon 1 JustJoe

注意, ResultSet 只按“杯”的次序排列。因此,很难保 证杯数相同项目的次序。例如,都是3杯的 John、Alex 和 jDuke 项目可能以 任何次序出现。这 3 个项目将在 4 杯或 4 杯以上项目之后,在 2 杯或 2 杯以 下的项目之后(记住,是按降序排列),实际上,也只能做到这些。

本节的练习包括检查 JJJJData表和生成报告的 完整应用程序的源代码。

练习

 

  1. 数据检索

 

本节结束时,要记住:

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? 如上所述,在用:识别机器或服务器以及基 本上用来识别数据库时,基本代码jdbc:: 显得非常精炼。实际上,其内容取决于专用驱动程序,并会因为产生“没有合适 的驱动程序”的错误等类途径问题使人不知所措,倍受困扰。以上述的范例中使 用的 Cloudscape URL 为例:

jdbc:cloudscape:rmi:jGuru
上述代码会解译成
jdbc:    :      jdbc:     cloudscape:rmi:     jGuru
这个相当简单,主要是因为客户程序且服务器都在相同的机器上运行。在低于第 四种类型的驱动程序中常常见到类似的 URL,因为要涉及到另外的设置,且定位 服务器要求的信息要从设置信息处获得。

 

即使在这里,情况并不总是令人满意。支持远程(并且甚至当地)连接的大多 数 DBMS 引擎,都使用TCP / IP(传输控制协议/ Internet协议)端口来做到 这一点。事实上,即使 Cloudscape 也在启动后使用cloudscape:rmi: subprotocol; run netstat,并可以在 1099 端口上看到。像任何 其他的套接字程序一样,DBMS 引擎可以随意确定要使用的端口。除了通常使用 的 TCP/IP 标准外,也可以使用其他的通信协议。例如, DB2 就可以在几个平 台上使用 APPC (Advanced Program to Program Communication,先进的程序 间通讯)。

在应用程序试着连接网络或 Internet 服务器时,必须提供识别/存储单元信 息。常见的 JDBC 方法是使用//host:port/subsubname,这里的主 机是 IP 地址或 DNS ( Domain Name Service,域名服务)或其他的可定位名 称。在驱动程序/数据库文档里查找默认端口,且记住系统管理员有权决定使用 不同的端口。这里的数据库就是 subsubname,而驱动程序的编写者可以随意在 他们自己的句法中添加其他属性。再次使用 Cloudscape 为例,该代码用来创建 数据库:

jdbc:cloudscape:rmi:jGuru;create=true
;create=true部分是使用 Cloudscape 句法的属性。含义是:在文档 中,检查出驱动程序和数据库。

 

“连接”在收集无用数据时自动关闭,但审慎的程序员会始终明确地关闭“连 接”以直接地确保节省资源。注意,虽然 API 会明确地表示关闭“连接”以“立 即释放数据库和 JDBC 资源”,但 JDBC 会建议关闭“连接”和“语句”。

和其他 JDBC API 的重要区域一样,“连接”也是界面。很多的程序员想 知道对象的起源处,因为界面不能用具体事例来予以说明。扼要的答案是: JDBC 驱动程序实现了界面并在请求时返回实际对象。 这同时说明了为什么应用程序编 译时十分完整,运行时却问题成堆:因为代码是参照标准接口编译的,只有装载 并运行程序和驱动程序时才能得到实际的效果。

“连接”界面控制的区域

 

前面的大多数章节都介绍了 DriverManagergetConnection() 方法的设置。“连接”本身负责的区域包括:

创建“语句”、 PreparedStatementCallableStatement (和存储过程一起使用)实例。

获取 DatabaseMetadata 对象。

通过 commit()rollback() 方法控制事务。

设置事务涉及的隔离级别。

在给定数据库的本地非标准语言中,甚至有获取任何 SQL 语句的方法,该方 法的适当名称是 nativeSQL()。本课程随后的部分会介绍这些区 域。

 

在继续介绍前,先讲解“ JDBC 2.0 可选包”引进的新 DataSource 类。该规范说明建议将 DataSource 用作获取“连接”的方法,并 实际上提到了反对当前的 DriverManager/Connection 方法。虽 然 JDBC 程序员应该知道这点,甚至可能也在大多数 J2EE 环境中使用---但令 人惊奇的是 DriverManager 方法不久就被废弃了。

连接信息概述

 

从上面获取“连接”对象所需信息的介绍中可知,对信息进行固定编码不是个 令人满意的决定。以下练习给出了在两个通用的编程方案中获取该信息的两种方 法---即使用 ResourceBundle 和/或直接从最终用户处获取。

 

您可能想知道,练习中给登录名和密码设置的“sa”和“admin”是 Cloudscape 的默认值还是胡乱设置的。答案是,在逻辑框以外,没有启用 Cloudscape 的验证/安全。必须亲自对它进行设置。否则,它就会忽视这些无效 参数和属性。从一开始,就已经引入了这些有效的虚拟名称,以介绍 JDBC 标准 “连接”参数。这再次强调了检查驱动程序和数据库文档的重要性。第二个答案 是,如同在很多其他区域中一样,编程时可能有镜像,但没有什么魔法。

练习

 

  1. 连接信息概述---批处理
  2. 连接信息概述---交互e

语句、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(INSERTUPDATEDELETE) 语句、DDL 语句(如 CREATE TABLEDROP TABLEALTER TABLE 语句)的方法来说,executeUpdate() 大概不太受人欢迎。无论如何,它用于所有这些方面;实际上,根据经验,它应 用于不返回 ResultSet 的任何语句。

JDBC 对类型进行定义以匹配 SQL 数据类型。这些定义必须适合于数据,以避 免出现技术问题、意外结果,并能促进工作效率。可用和合适类型的更详尽信息, 请查看“Java- SQL 类型等效性”。

executeUpdate() 返回 int 值,它包含 INSERT、 UPDATE 或 DELETE 语句作用过的行数,或不返回任何东西的 SQL 语句(如 DDL 语句)的零。

练习

 

  1. 使用 executeUpdate()

数据库查询

 

executeQuery() 应用于返回 ResultSet 的“语句”,基本上是 SELECT 语句。

executeQuery()返回的默认 ResultSet 对象拥 有只向前移动的光标,该光标使用 next() 方法。应该注意, executeQuery()始终返回非空的 ResultSet。新手 常常比较 ResultSet 和零值,以确定是否已返回行。如果没有驱 动程序错误,就决不会出现这类情况。 next() 返回布尔值,在 另一行生效时是 true;在 ResultSet 耗尽时为 false。如果只希望返回单行,就可以使用 if 语 句。否则,通常使用 while 循环:

int iCount = 0;while( myResultSet.next() ){  // retrieve column data  // do something with it  iCount++;}if( iCount == 0 ){  System.out.println(    "myResultSet returned no data.");}elseif( bNoErrorsOrExceptionsOrEarlyTerminations ){  System.out.println(    "All rows from myResultSet were processed.");}

 

应该按从左到右的语句(次序和 SELECT 中的相同)次序进行读 取列,并可以按列名称或索引获取列。尽管列名可能更易于理解,但使用索引更 有效率(按 1,2,3 而非 0,1,2,3...的索引顺序)。数据库和驱动程序都可能变 化,如果没有可移植性,在默认的 ResultSet 中可能一次仅仅只 能获取一行甚至是该行中的一列。

ResultSetgetXXX() 方法用来检索列数据。JDBC 对类型进行定义,以和 SQL数据类型匹配,每个数据类型都有 getXXX() 方法。可用类型和合适类型的更详尽信息,请查看“Java-SQL类 型等效性”。

“语句”一次仅仅打开一个 ResultSet,常常对新建数据重复 使用相同的 ResultSet。应该确保从 ResultSet 中 获取全部所需数据,再通过相关的“语句”执行另一个查询。在重复执行和 Statement.close() 时,“语句”应该自动地关闭 ResultSet, 但在不再需要数据时可以亲自关闭 ResultSet。审慎的程序员可能 始终明确地关闭 ResultSet

ResultSet 也可以返回元数据,它是有关 ResultSet 本身和所包含数据的信息。在“ ResultSet 元数据” 将更进一步地对此进行介绍。

练习

 

  1. 选择数据和显示信息

筹备语句

 

PreparedStatement 是“语句”的子界面,有以下好处:

已包含的 SQL 会被发送到数据库,并预先进行编译或筹备。由此开始发送已 筹备的 SQL,本步骤会被省略。更有活力的“语句”对每个执行都要求本步骤。 根据 DB 引擎的不同,SQL 可能会进行高速缓存并重复使用。即使对不同的 PreparedStatement 也是如此,并且大部分工作由 DB 引擎而不是 驱动程序完成。

PreparedStatement 可以处理列值的 IN 参数, 该参数的作用非常类似于方法的参数。

例如,PreparedStatement 能处理直接运行很易于出错的数据 变换,这些数据变换是迅速地在 SQL 上创建的;并以一种对开发者来说透明的方 式处理引用和日期。

 

注意: SQL3 类型通常假设使用 DML 的筹备语句。

这里,有两个安装和获取筹备语句的范例:

pstmtU = con.prepareStatement(   "UPDATE myTable SET myStringColumn = ? " +    "WHERE myIntColumn = ?"  );pstmtQ = con.prepareStatement(   "SELECT myStringColumn FROM myTable " +    "WHERE myIntColumn = ? ");

 

问号,也被称作参数标志,是在语句执行前待设置的值。从 1 开始,按从左 到右的数字顺序对它们进行引用。PreparedStatementsetXXX() 方法用来设置 IN 参数,在进行更改前会保持所作设置。可用类型 和合适类型的更详尽信息,请查看“Java- SQL 类型等效性”。 下面是在上述语句中设置参数的范例:

pstmtU.setString( 1, "myString" );pstmtU.setInt( 2, 1024 );pstmtU.executeUpdate();pstmtQ.setInt( 1, 1024 );pstmtQ.executeQuery();

 

也可以筹备没有参数的语句。注意, PreparedStatement 拥有 自己的 execute 方法系列版本,它由于要设置参数所以没有自变 数。记住, PreparedStatement 是从“语句”继承下来的,并包 括了“语句”所有的功能。一般而言,在查询运行了多次仅有相同列的值发生变 化或重复运行同一查询时,请考虑使用筹备语句。

练习

 

  1. 使用筹备"语句"

 

Java - SQL类型等效性

 

JDBC 的" 类型"定义为转换成标准 Java 类型,提供了属类的 SQL 类型。通常是直接 确定所需的类型和方法。以下两个表显示用于获取每个数据类型的常规 ResultSet 方法。典型 setxxx() 方法的格式相同。

通用的 SQL 类型---标准检索方法

 

SQL 类型 Java方法 BIGINT getLong() BINARY getBytes() BIT getBoolean() CHAR getString() DATE getDate() DECIMAL getBigDecimal() DOUBLE getDouble() FLOAT getDouble() INTEGER getInt() LONGVARBINARY getBytes() LONGVARCHAR getString() NUMERIC getBigDecimal() OTHER getObject() REAL getFloat() SMALLINT getShort() TIME getTime() TIMESTAMP getTimestamp() TINYINT getByte() VARBINARY getBytes() VARCHAR getString()

为了显示, ResultSet.getString() 也可以应用于上述类型, 可能对 OTHER 例外。

 

SQL3 类型---检索方法

 

SQL 类型 Java 方法 ARRAY getArray() BLOB getBlob() CLOB getClob() DISTINCT getUnderlyingType() REF getRef() STRUCT (castToStruct)getObject() JAVA_OBJECT (castToObjectType)getObject()

ResultSet.getObject() 也可以用于两个表中列出的任何类型。

这些看起来非常清晰明了,难度也不大,但专业程序设计员应该花些时间阅读 映射 Java 的 SQL 数据类型和映射 SQL 及 Java "类型"。尤其要通过" ResultSet.getXXX() 方法"检查"转换"表,以查看可用选项的种类。

对于应用定位程序的 SQL3 类型,因为文档上令 人遗憾的缺陷,人们常常对它发出这样的疑问:"开始时,该如何将类型输入数据 库呢?"最好的答案是,检查它们对应的类(例如, BLOBBlob 类),并根据 getXXX() 方法找出 setXXX() 方法应用的具体化数据,通常带有 PreparedStatement。就 Blob 而言是 getBinaryStream()getBytes(),因此对应地就 有了 setBinaryStream()setBytes()。更多的 信息和范例代码,请查看 LOB 和本部分相关的练习。

JDBC 例外类型以及例外处理

 

"我再不愿意想这件事情。"---一般而言,实话实说的开发者对问到例外/错误 处理时的反应很可能就是这句话。它说明了很难正确地进行处理,还常常会吃力 不讨好。这对于编制优质的应用程序也很关键。

本课程中的练习突出强调专门的 JDBC 区域,未对生产质量提出要求。同时, 从第一个练习开始介绍了异常处理的标准。不过,标准并不完整,因此介绍了三 种 SQLExceptions 以作弥补。

注意,加入 JDBC 2.0 中的第四种类型,BatchUpdateException, 在"批更新装置"中介绍。

SQL 例外

 

java.sql包中的许多方法都会形成 SQLException,它和任何其他"例外"一样要求尝试/捕捉语句块。其 目的是描述数据库或驱动程序错误(例如,SQL 语法)。除了从 Throwable 继承下来的标准 getMessage()SQLException 还 有两种提供详细资料的方法:一种方法是获取(或链接)其他的例外,一种方法 是设置其他的例外。

getSQLState()返回基于 X/ Open SQL 规范说明的 SQLState 标识符。这些会在 DBMS 手册中列出,或在"资源"中 寻找 SQLStates 的信息。

getErrorCode() 用来检索特定于供应商的错误代码。

getNextException() 检索下一个 SQLException, 或在没有 SQLException时为零值。程序和数据库间很多的情况都 可能出错。本方法允许跟踪全部发生的问题。

setNextException() 允许程序员给链添加 SQLException

 

这些方法应该相当直接了当。典型的捕捉代码看起来如下所示:

    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 警告

 

SQLWarningSQLException 的子类,但成因和其他的例外不一样。程序员必 须明确地索取警告。"连接"、"语句"和 ResultSet 全都拥有允许 检索的 getWarnings() 方法。还有避免复制检索的 clearWarnings()方法。SQLWarning 类本身仅仅添 加了方法getNextWarning()setNextWarning()

SQLWarning 非常类似于传统的编译程序警告:发生不完全正 确的某些事情,但严重结果又不足以结束处理。重要性程度是否达到了需要调查 的地步,取决于操作和上下文。"可卷动的结果集合" 部分提到了 SQLWarning的范例。

"语句"在进行下一个执行时自动地清除警告。ResultSet 每次 访问新行时都清除警告。API 文档对于"连接"而言是静态的。为谨慎起见,请在 警告给出后发布 clearWarnings()

获取 SQLWarning 的典型代码如下:

    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

 

数据截断

 

DataTruncationSQLWarning 有点奇特的子类。如果出现在读取时,则发布 SQLWarning;如果发生在写入/更新时,则给出 SQLException。 实际上,它仅仅涉及写入/更新操作,因此按 SQLException 的方 式进行处理,并始终处于 SQLState 为 01004 的状态。

数据截断主要表示读取或写入的信息要比请求的少。一些数据库/驱动程序会 接受超过列容量的数据,它们会将数据截断,再写入已截断数据,并通过 DataTruncation SQLException 报告"You gave me too much data, but I handled it."

DataTruncation 类包括下列与截断数据信息有关的方法: getDataSize(), getIndex(), getParameter(), getRead(), and getTransferSize()

 

样本误差测试结果

 

下面的是一组从 Cloudscape,UDB2/NT 和 DB2/400处返回的、特定地用于提 示问题的实际错误信息。详情请查看本节 练习。

DELETE FROM JJJJTee
  WHERE Entry = 97

CS Result:
0 rows processed.

UDB2/NT 结果:
0 rows processed.

DB2/400 结果:
DELETE FROM JJJJTee WHERE Entry = 97
Statement Warnings:
[SQL0100] Row not found for DELETE.
SQL State: 02000
Vendor Error Code: 100
0 rows processed.

INSERT INTO JJJJTee
  VALUES (25, 'Rosa', 'Petite', 'Blue')

CS Result:
INSERT INTO JJJJTee VALUES (25, 'Rosa', 'Petite', 'Blue')
problems with executeUpdate:
已放弃语句,因为它已经在单值健或主键约束中引起了重复键值。
SQL State: 23500
Vendor Error Code: 20000

UDB2/NT 结果:
INSERT INTO JJJJTee VALUES (25, 'Rosa', 'Petite', 'Blue')
problems with executeUpdate:
[IBM][CLI Driver][DB2/NT] SQL0803N One or more values in the INSERT statement, UPDATE statement, or foreign key update caused by a DELETE statement are not valid because they would produce duplicate rows for a table with a primary key, unique constraint, or unique index. SQLSTATE=23505
SQL State: 23505
Vendor Error Code: -803

DB2/400 结果:
INSERT INTO JJJJTee VALUES (25, 'Rosa', 'Petite', 'Blue')
problems with executeUpdate:
[SQL0803] Duplicate key value specified.
SQL State: 23505
Vendor Error Code: -803

<UPDATE JJJJTee
  SET TColor = 'Black'
  WHERE TColor = 'Appetite'

CS Result:
0 rows processed.

UDB2/NT 结果:
0 rows processed.

DB2/400 结果:
UPDATE JJJJTee SET TColor = 'Black' WHERE TColor = 'Appetite'
Statement Warnings:
[SQL0100] Row not found for UPDATE.
SQL State: 02000
Vendor Error Code: 100
0 rows processed.

DROP TABLE IDontExist

CS Result: DROP TABLE IDontExist
problems with executeUpdate:
Table 'IDONTEXIST' does not exist.
SQL State: 42X05
Vendor Error Code: 20000

UDB2/NT 结果: DROP TABLE IDontExist
problems with executeUpdate:
[IBM][CLI Driver][DB2/NT] SQL0204N "userID.IDONTEXIST" is an undefined name. SQLSTATE=42704
SQL State: 42S02
Vendor Error Code: -204

DB2/400 结果:
DROP TABLE IDontExist
problems with executeUpdate:
[SQL0204] IDONTEXIST in JGURU type *FILE not found.
SQL State: 42704
Vendor Error Code: -204

UPDATE JJJJTee
  SET TSize = 'Small     Doppelganger'
  WHERE TSize = 'Small'

CS Result:
UPDATE JJJJTee SET TSize = 'Small     Doppelganger' WHERE TSize = 'Small'
problems with executeUpdate:
Non-blank characters were found while truncating string 'Small     Doppelganger' from length 22 to length 10.
SQL State: 22001
Vendor Error Code: 20000

UDB2/NT 结果:
UPDATE JJJJTee SET TSize = 'Small     Doppelganger' WHERE TSize = 'Small'
problems with executeUpdate:
[IBM][CLI Driver][DB2/NT] SQL0433N Value "Small     Doppelganger" is too long. SQLSTATE=22001
SQL State: 22001
Vendor Error Code: -433

DB2/400 结果:
UPDATE JJJJTee SET TSize = 'Small     Doppelganger' WHERE TSize = 'Small'
problems with executeUpdate:
[SQL0404] Value for column or variable TSIZE too long.
SQL State: 22001
Vendor Error Code: -404

UPDATE JJJJTee
  SET TSize = 'Small                 '
  WHERE TSize = 'Small'

CS Result:
3 rows processed.

UDB2/NT 结果:
3 rows processed.

DB2/400 结果:
3 rows processed.

DROP TSBLE BadSQL

CS Result:
DROP TSBLE BadSQL
problems with executeUpdate:
Syntax error: Encountered "TSBLE" at line 1, column 6.
SQL State: 42X01
Vendor Error Code: 20000

UDB2/NT 结果:
DROP TSBLE BadSQL
problems with executeUpdate:
[IBM][CLI Driver][DB2/NT] SQL0104N An unexpected token "TSBLE" was found following "DROP ". Expected tokens may include: "JOIN ". SQLSTATE=42601
SQL State: 42601
Vendor Error Code: -104

DB2/400 结果:
DROP TSBLE BadSQL
problems with executeUpdate:
[SQL0104] Token TSBLE was not valid. Valid tokens: DISTINCT DATA.
SQL State: 42601
Vendor Error Code: -104

练习

 

  1. 处理 SQLExceptions 和 SQLWarnings

元数据

 

元数据是关于数据的数据(或信息)。JDBC 允许程序员通过元数据类去发现 关于数据库和任何给定 ResultSet 的大量信息。

数据库元数据

 

为了发现数据库的信息,必须获取 DatabaseMetaData 对象。一旦程序已经获取有效的"连接",该代码就会获取元数据对象:

  DatabaseMetaData dbmd = con.getMetaData();

 

好在只调用所希望信息的方法,就可以做到这一点。大部分的问题是:

DatabaseMetaData类中有大约 150 种方法。无疑,精通(乃至 了解)可用信息本身就是一个很重的任务。不过,初步了解一下 API 可能有所帮 助。

许多方法返回 ResultSet,对此程序员必须逐步处理以获取特 定的信息。

部分方法,包括返回数据库和表组件的信息,使用的名称格式十分混乱。根据 数据库的不同,信息可能采取大写字母、小写字母或大小写混合的形式,格式是 区分大小写的。结果,必须调用方法在试图获取信息前,找出信息存储的方式。

 

虽然这可能有些不令人满意,但可以轻松获得最常用的 DatabaseMetaData 信息,如数据库名称、驱动程序名称、版本、可用的最大连接数、 SQL一致性等 等。很多程序根本不需要此类信息。注意,给定的 DBMS 可能不为所有方法提供 信息,检查会返回零值或空字符串的对象。

对本课程中的程序已提供了链接,这些程序使用下面"练习"部分的 DatabaseMetaData。 转到每个练习的程序"解决方案"部分,并在 DatabaseMetaData 中 搜索用法范例。

练习

 

  1. 连接信息概述---批处理
  2. 确定可用的标量函数
  3. 使用批处理更新
  4. 用可卷动的 ResultSets 分页

 

ResultSet 元数据

 

为了发现给定 ResultSet 的信息,必须获取 ResultSetMetaData 对象。一旦程序已经获取有效的 ResultSet ,该代码就会获取元 数据对象:

  ResultSetMetaData rsmd = rs.getMetaData();

 

ResultSetMetaDataDatabaseMetaData 更易 于管理,约有 25 种方法。使用 ResultSetMetaData 后,应用程 序可以找到所返回列的数、各列建议的显示大小、列名、列类型等等。注意,给 定的 DBMS 可能不为所有方法提供信息,所以检查会返回零值或空字符串的对象。

对本课程中的程序已提供了链接,这些程序使用下面"练习"部分的 ResultSetMetaData。 转到每个练习的程序"解决方案"部分,并在 ResultSetMetaData 中搜索用法范例。

练习

 

  1. 连接信息概述---批处理
  2. 连接信息概述---交互
  3. 选择数据和显示信息
  4. 用可卷动的 ResultSets 分页

 

扩展符句法和标量函数

 

大多数的数据库提供标量函数(有时称为内部函数),用于执行列的特定值上 的操作,甚至提供迅速创建列的值。JDBC 规范支持不同的数学值、字符串、系统、 时间和日期、X/Open Call Level Interface(打开调用级别界面,CLL)指定的 类型转换功能;如果底层的 DBMS 支持某功能,则 JDBC Compliant 驱动程序也 必须支持。这些功能的名称应该和 X/Open 名称匹配,虽然情况不总是这样。对 其功能或从应用程序切换到数据库而言,标量函数非常重要。

JDBC 提供这些方法,以确定标量函数:getNumericFunctions()getStringFunctions()getSystemFunctions()getTimeDateFunctions()、和supportsConvert() 的两个版本。getXXXFunctions() 以逗号分开的字符串返回函数名。

因为不同的数据库对标量函数调用使用不同的语法,所以 JDBC 定义了专门的 扩展符语法。JDBC驱动程序会理解这些语法,并映射到适当的语法以找出基础数 据库。扩展符也用于 LIKE 字符、日期与时间文字、存储的步骤调 用和外部连接。标量函数的扩展符是 fn。实际的函数名和自变数 都放在花括号内,如{ fn }

在 SQL 语句中,标量函数通常和列一起使用。例如, PI() 数 值函数就是如此:

UPDATE myTable
SET circularVal = squared * { fn PI() }
...

or

SELECT { fn concat( string, "bean" ) }
...

受到支持的功能,请查阅 DBMS 手册。

练习

 

  1. 确定可用的标量函数

 

存储过程

 

存储过程是用户生成的函数或步骤,在注册数据库后,可以称为客户应用程 序。这些过程很有价值,因为它们可以将工作切换到服务器,并减少编码,对于 复杂的操作尤其如此。令人遗憾的是,对于创建存储过程而言,没有标准的方式、 要求甚至语言。而且并非全部的数据库都支持它们。在这种情况下,就没有办法 创建常用的练习,所以本节只限于讨论使用 JDBC 标准方法调用存储过程及其代 码片断。当然,创建存储过程可以一次性操作成功,并提示其名称和要求的参数 类型。

元数据支持

 

有几种 DatabaseMetaData 方法可以返回特定数据给存储过程 提供的支持的信息。

supportsStoredProcedures() 确定 DBMS 是否支持 JDBC 标准 的存储过程扩展符语法。

getProcedures() 返回可用存储过程的列表,同时 getProcedureColumns() 描述参数和结果。

getProcedureTerm() 会将供应商对存储过程的建议名称通知给 程序员。

参数详情

 

在调用时,如同标准方法或功能一样,存储过程可以接收零、更多自变数或参 数,也称为 IN 参数。它们可以返回 ResultSet、更新计数、结果 参数、和/或零或更多 OUT 参数。另外,过程过程可以拥有 INOUT 参数,而在这 样情况下会向它发送值,并以相同的变量返回不同的值。IN、OUT 和 INOUT 参数 都封装在括号表达式中,仅仅以数字作为区别。该数字相当于参数标志出现的顺 序(?---问号),从 1 而不是 0 开始。

扩展符句法x

 

"扩展符语法和标量函数"中曾经提到,存储过程要 求 JDBC 扩展符语法以进行标准调用。驱动程序会再次处理实际映射。基本格式 的组成是: call sp_name? = call sp_name 加上可选参数。全部都以花括号括起来。 下面是几个范例形式,下一段将更详细 地进行介绍。

A - 不带参数,它返回 ResultSet 或行数。

     { call sp_A }
B - 单一参数,返回结果参数。假设为 int 结果参数和 String IN 参数:

 

     { ? = call sp_B( ? ) }
C - 多参数,它返回 ResultSet 或行数。假设为 int IN、 OUT 和 INOUT 参数:

 

     { call sp_C( ? ? ? ) }

 

CallableStatement

 

要向数据库发送实际的存储过程执行请求,可使用 CallableStatement, 它扩展了 PreparedStatement。在创建 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 参数用从 PreparedStatement 继承下来 的 setXXX() 设置。

必须使用 CallableStatement.registerOutParameter() 方法 之一注册 OUT 参数。

必须设置和注册 INOUT 参数。

实际的调用将根据预期结果照例采用 executeQuery()executeUpdate()execute()

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 parametercstmt.registerOutParameter( 1, Types.INTEGER );// String IN parametercstmt.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 parametercstmt.setInt( 1, 333 );// register int OUT parametercstmt.registerOutParameter( 2, Types.INTEGER );// set int INOUT parametercstmt.setInt( 3, 666 );// register int INOUT parametercstmt.registerOutParameter( 3, Types.INTEGER );
就无返回而言 ( OUT 和 INOUT 除了:)
cstmt.execute(); // could use executeUpdate()// get int OUT and INOUTint iOUT = cstmt.getInt( 2 );int iINOUT = cstmt.getInt( 3 );
就已返回的 ResultSet而言:
ResultSet rs = cstmt.executeQuery();// get int OUT and INOUTint iOUT = cstmt.getInt( 2 );int iINOUT = cstmt.getInt( 3 );
就已返回的更新计数而言:
int iUC = cstmt.executeUpdate();// get int OUT and INOUTint iOUT = cstmt.getInt( 2 );int iINOUT = cstmt.getInt( 3 );

 

所有这些都是很细致的工作,但格式应该明确。

事务

 

在SQL术语中,事务是逻辑工作单元(logical unit of work,LUW)构成的一 个或多个语句。这在某种含义上意味着,一切都是事务。不过,通常而言,术语 事务用来表示或全或无的系列操作;也就是说,要么一切十分成功,要么什么也 没有发生。

典型的事务是从银行帐户提款,并存放到另一个。只要提款完成,金额就消失 了。另一个范例是复式簿记记帐法中的借方和贷方:借方和贷方都必须完成。第 三个方面,即本节练习中的内容,是保证 INSERTUPDATE、 或 DELETE 的无错误操作。

虽然一些 SQL非标准语言有专门的开始和结束事务语句,但总的来说事务会从 程序开始持续进行到语句联锁。并从该点,开始新的事务。这是 JDBC 所使用的 模型。JDBC 驱动程序的默认值是 autocommit,表示每个 SQL 语句的 结果一旦执行就永久保留。这是本课程到现在为止无需考虑事务的原因,而且在 很多情况下都很能让人接受。

注意: 在 autocommit 模式中,联锁出现于"语句"完成时。"语句" 返回 ResultSet 时,直到最后一行已经检索或 ResultSet 关闭,"语句"才完成。

"连接"的 setAutoCommit(布尔型的autoCommit)方法是处理事务的关键。每 个语句采用Connection.setAutoCommit(true)联锁;采用 Connection.setAutoCommit(false)进行编程事务控制。可以随意 调用该方法,必要时,可在程序多次调用。调用Connection.setAutoCommit(false) 后,Connection.commit()Connection.rollback() 就用来控制LUWs(应该是LUsW,正如indexes应该是indices 一样,但应该具体情况具体分析)。

联锁

 

一旦 autocommit 设置为 false,全部数据库 DML 语句在联锁前都可以视为 暂时性语句。JDBC 支持带 Connection.commit() 方法的联锁。最后一次联锁到数据库后,上述基本上是全部永久安置所必须的一 切内容,虽然计时偶而会出现错误。以前,很多的数据库设置了锁,甚至对读取 操作也是如此,以防止其他用户访问同一数据。自动的独占性读取锁定在时间的 这一点上相当稀少,但是从那时开始到现在仍然适用的好口号是"早联锁,常联锁"。 当然,不能太早。

注意: 事务中的 DDL 语句可以忽视或形成联锁。该特性是由 DBMS 决定的, 并可以通过 DatabaseMetaData.dataDefinitionCausesTransactionCommit()DatabaseMetaData.dataDefinitionIgnoredInTransactions() 显示出来。 避免意外结果的一种方法是区分 DML 和 DDL 事务。

回退

 

Connection.rollback() 用来删除先前联锁或回退后已执行的操作。在发生异常或程序检测出错误状态或 数据错误时,请使用该方法。

并行

 

大多数的 DBMS 允许多个用户同时对数据进行操作。有时,开发者不太关心数 据库并行问题。(在数据开始消失或数据库数据库发生异常时,开发者常常会处 于冒险状态,至少也会同主管争执。) 并行的级别和类型对性能也有影响。

JDBC 识别下列控制并行性的事务 隔离级别。

TRANSACTION_NONE

TRANSACTION_READ_COMMITTED

TRANSACTION_READ_UNCOMMITTED

TRANSACTION_REPEATABLE_READ

TRANSACTION_SERIALIZABLE

 

使用 Connection getTransactionIsolation()setTransactionIsolation( int 级别)方法确定并设置期望的隔离 级别。JDBC 驱动程序具备默认值的隔离级别,常常是基础数据库的默认值。并非 全部的数据库都支持上述所有设置。

在数据库操作中适当而有效的并行处理极端重要,很多应用程序根本无法达到 要求。令人遗憾的是,全面讨论几乎要占很大的篇幅,所以请参看 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

练习

 

  1. 使用事务

批更新装置

 

"批更新装置"是 JDBC 2.0 的新功能,允许以多个语句为单位向数据库进行发 送,从而提高性能。注意,实现该功能不需要驱动程序,采用了驱动程序并不比 常规提交更有效率。即使如此,使用批更新无需额外努力(报告除外),且潜在 的好处可能很有价值。驱动程序的支持可以使用 DatabaseMetaData.supportsBatchUpdates() 方法确定。

JDBC 2.0 语句是使用命令的自动关联列表创建的。addBatch()clearBatch()executeBatch() 方法用于操作和 执行列表。executeBatch()) 则返回 int 阵列,该 阵列为每个已执行的 SQL 语句提供完成或错误信息。JDBC 的建议是在批更新"供 适当的错误处理"时将 autocommit 设置为 false。这样做也可以获得全部的事务 处理好处。

可以在更新计数阵列中返回的 int 值是:

-3--操作错误。驱动程序的拥有可以在第一个错误处停止 的选项,会给出 BatchUpdateException 或报告错误并继续执行。 仅在出现后一种情况时会显示该值。

-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

 

Statement.executeBatch() 可以给出 BatchUpdateException, 它是 SQLException 的子类。它唯一的额外方法是 getUpdateCounts(), 该方法允许程序员获取供报告用的更新计数阵列。您可能已经注意到了,批处理 在捕捉例外上还有问题。BatchUpdateException 的一个奇怪之处 是,它没有给其他的 BatchUpdateException 提供链接方法,只继 承 SQLException.getNextException()。使用"批更新装置"的代码 也可以捕捉和处理 SQLException

典型 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从 DatabaseMetaData.supportsBatchUpdates() 处返回 false。以下练习已经在 Cloudscape 和 DB2/400 上进行了试验。

练习

 

  1. 使用批处理更新

 

可卷动结果集

 

到目前为止,所有的 ResultSet 都已经以连续的方式进行了运 用,从头至尾使用 ResultSet.next() 获取各行。如在语句、 ResultSet 和与数据库互动中介绍的,在本课程自始至终也可以看 到, ResultSet 是通过语句获取的,通常使用 executeQuery 方法。到现在为止,语句的创建都是使用

stmt = con.createStatement();
该方法是 JDBC 1.0 中唯一可用的方法。在 JDBC 2.0 中,存在着允许创建可卷 动和/或可更新的 ResultSet 新方法。
createStatement(    int resultSetType,    int resultSetConcurrency )
resultSetType 可以是

ResultSet.TYPE_FORWARD_ONLY--这是默认值,和 JDBC 1.0 中的一样:仅转 交移动,列一般地仅能读取一次。在 ResultSet.next() 返回 false 时, ResultSet 数据就不再有效并自动关 闭。

ResultSet.TYPE_SCROLL_INSENSITIVE 允许创建 ResultSet, 其中的光标可以向后、向前和随机移动。这是静态数据:数据库中对当前 ResultSet 中选定的行进行的任何更改都是不可见的。也就是说, ResultSet 对数据修改不敏感。

ResultSet.TYPE_SCROLL_SENSITIVE 允许创建 ResultSet,其 中的光标可以向后、向前和随机移动。这提供了数据的动态视图:数据库中对当 前 ResultSet 中选定的行进行的任何更改都是可见的。也就是说, ResultSet 对数据修改很敏感。

resultSetConcurrency 可以是

ResultSet.CONCUR_READ_ONLY - 这是默认值,和 JDBC 1.0中的一样:

ResultSet.CONCUR_UPDATABLE 允许通过新的 ResultSet 方法 和定位性能对编程数据进行更改。

可更新的 ResultSet 优点、缺点都有,本课程不作深入讨论。 更多的信息,请查看 Java 教程或 JDBC 教程和 参考资料(第二版)联机版本中高级教程的第 3.3 部分。

 

注意,已请求的 ResultSet 类型,即使受到驱动程序的支持, 也可能无法返回。如果出现该情况,驱动程序会在连接时发出 SQLWarning。 另外, DatabaseMetaData.supportsResultSetType() 可用于确定 驱动程序支持的 ResultSet类型, ResultSet.getType() 提供实际返回的 ResultSet 的类型。详情请查看请 求未支持的特性。

可卷动的 ResultSet 的获取方式如任何其他的一样,通常都是 通过 Statement.executeQuery() 进行。不过,有了 ResultSet, 就可以使用以下方法:

 

absolute()

afterLast()

beforeFirst()

first()

getRow()

isAfterLast()

isBeforeFirst()

isFirst()

isLast()

last()

moveToCurrentRow()--仅对可更新的 ResultSet 有效。

moveToInsertRow()--仅对可更新的 ResultSet 有效。

previous()

relative()

这些方法的详情请查看 ResultSet。

 

用法注释

 

可卷动 ResultSets 的驱动程序性能和但实现级别会变化,有 时很显著。请检查文档。下面是在使用可卷动的 ResultSets 时应 考虑的其他几个因素,但决不是全部。

可卷动的 ResultSet,正如不可卷动的 ResultSet 一样,检索时定位在第一行前。

所有的行检索时,语句才视为完成。这在 ResultSet.next() 检索最后一行时发生。一些驱动程序在 autocommit 开启时将此视为联锁该语句。 结果, ResultSet 会关闭,并在下一个访问尝试时给出 SQLException。 为了可移植性,请将 autocommit 设置为 false。

ResultSet.getRow() 会在某些(甚至全部)位置返回零。这尤 其意味着,使用已赋值的 ResultSet.last()ResultSet.getRow() 序列在数据库中(甚至在同一数据库的驱动程序中)获取的行数不太可靠。

ResultSet.absolute() 在传递零时会给出 SQLException

ResultSet.relative() 不会更改光标的位置。不过,至少一 个供应商会不检查零值而从 ResultSet.absolute() 中调用 ResultSet.relative()。请考虑潜在的(笔者有此教训)结果。

练习

 

  1. 用可卷动的 ResultSet 分页

 

LOB

 

JDBC 2.0包括处理若干 SQL3 数据类型的类。本节介绍 LOB 或大对 象。LOB 的定义有两种类型:BLOB---二进制大对象和 CLOB ---字符大对象。

从经典关系数据库理论的来看,Clob 属于临界类型---字符很多--- Blob 实 际上根本不是一种类型。全部所知就是,Blob包含一些可以是任何内容的字节数。 显然,这会损害数据独立性的概念。在有其他非常易于接受的方法,使用数据库 跟踪基本上是图形、声频或其他的类型的二进制文件时,尤其如此。注意,没有 机制可以阻止向视为图像的 Blob 中写入声音文件。也没有机制可以去了解原始 源的名称或任何类似的考虑。

定位器

 

SQL 定位器类型在概念上类似于指针或记录实体的其他信息。JDBC 开发者不 必处理定位器,但有助于理解概念。因为定位器实际上是 JDBC 驱动程序在阵列、 BlobClob 列中预期会找到的内容。也就是说, 实际数据不会在 ResultSet 转到下页,只是转到定位器。可以明 确地按需要请求待返回的 LOB 数据,这被称作具体化数据。无疑,这比将每列大 量未知的字节进行转下页处理要有效的多。实际数据被 DBMS 存储到"其它地方" 并从该处检索。

Clob

 

Clob 是 SQL CLOB 的 JDBC 界面映射。Clob 通过 ResultSetCallableStatementgetClob() 方法获取。 Clob 具有各种方法,以获取数据的子字符串、数据的长度、另一 个 Clob 的位置或当前的 Clob 中的字符串。这些 方法的运行不需要具体化数据。要具体化数据,可使用 getAsciiStream()getCharacterStream()(就Unicode流而言),然后从返回的流 中建造可用对象。

对于 Clob 存储,请使用 PreparedStatement 中的 setClob(),或可更新 ResultSet 中的 updateObject()。讨论到此为止,从根本上而言,范例会检索一行 中的 Clob,并将其放到同一或不同表中的另一行中。

但是首先如何填充 Clob 呢?Clob getAsciiStream()getCharacterStream() 方法 可以解决这一问题:使用 PreparedStatementsetAsciiStream()setCharacterStream() 方法填充Clob

Blob

 

Blob 是 SQL BLOB 的 JDBC 界面映射。Blob 通过 ResultSetCallableStatementgetBlob() 方法获取。 Blob 具有获取字节数、确定另一个 Blob 起动位置 或当前 Blob 字节阵列的方法,这些方法的运行不需要具体化数 据。要具体化数据,可使用 getBinaryStream()getBytes() (就部分或全部 Blob 而言),然后从返回的流或字节中建造可用 对象。

对于 Blob 存储,请使用 PreparedStatement中 的 setBlob(),或可更新 ResultSet 中的 updateObject。其讨论也到此为止,从根本上而言,范例会检索一 行中的 Blob,并将其放到同一或不同表中的另一行中。

首先,Blob 数据如何起作用呢?请再次检查 Blob 方法,这次使用 getBinaryStream()getBytes(): 使用 PreparedStatementsetBinaryStream()setBytes() 方法填充 Blob

 

本课程使用的 Cloudscape 版本不支持 SQL3 数据类型。以下练习已对 UDB2/NT 和 DB2/400 进行了测试。

练习

 

  1. 在 Blob 中存储图像
  2. 从 Blob 中检索并显示图像

 

SQL一致性

 

JDBC Compliant 驱动程序的基本要求是,必须支持 ANSI SQL- 92 Entry Level,它基本上是SQL-89 的第2级。以下是不详尽的、基本的 SELECTINSERTUPDATEDELETE 语句以 外的 SQL-92 Entry Level 功能的列表。

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 子查询。

充分支持功能中的 Distinct

支持并集。

更多完整数据,包括 SQL- 92 的中级和完全级别详情,请参看: FIPS PUB 127-2: 数据库语言 SQL 的标准。

 

DatabaseMetaData 方法 supportsANSI92EntryLevelSQL()supportsANSI92IntermediateSQL()supportsANSI92FullSQL() 用于在运行时发现 SQL 的一致性级别,以及特定数据库和驱动程序的性能。 JDBC Compliant 驱动程序必须对 supportsANSI92EntryLevelSQL() 返回 true。

另外,ODBC 定义的 SQL 语法支持级别可以通过 DatabaseMetaData 方法 supportsMinimumSQLGrammar()supportsCoreSQLGrammar()supportsExtendedSQLGrammar() 确定下来。JDBC Compliant 驱动程序必须对 supportsMinimumSQLGrammar() 返回 true。您可 能想知道, ODBC 是不是纯粹的 Microsoft 标准。在 AcuODBC SQL 一致性中可以找到显示 SQL 语法级别的表。 ODBC 3.51 版本中可以找到更多的 ODBC 信息。

根据应用程序的不同,可能会返回不支持某些功能的消息,或使用不同的算法 根据 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 可选包二进制文件。DataSourceConnectionPoolDataSourcePooledConnection 的更多信 息,请查看 Maydene Fisher 编著的 JDBC 2.0 Optional Package 的前 3 部分。

如文中介绍,使用 DataSourcePooledConnection 的程序主干代码是:

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(); }}

 

记住,javax.naming 包属于 Java 命名和目录界面TM ( JNDI ),也是 JDK 1.3 的内容。此外,请记住连接池类会越过 close() 方法,并将连接标记为可用。这就是确保 close() 被调用的重要 性。否则,池将连接视为"使用中",并在第二次请求时创建新的连接,这就损失 了池的好处。

上述的代码可以这么直接了当,是因为预计可以由数据库管理员使用 DBMS 供 应商提供的工具来创建 DataSource (无论是否经过池处理)。令 人遗憾的是,这使得我们几乎不可能给出常规的练习,因为每个供应商可以提供 不同的任务完成方法。使用 JNDI 设置 DataSource 的介绍,请查 看 高级教程 3.7 部分的 3.7.3 节。

上述应用程序代码的 JSP 版本非常类似:

...

 

在显示信息的应用程序中,应该创建粒媒,该粒媒可以处理查询、于请求时可 以返回包含行里的列和/或以单一字符串按照需要报告列的 QueryRows 或类似对象。粒媒可以根据控制的优先级和功能的分类将 DataSource 或者 Connection 作为自变数处理。.

如果有分页显示,就可以使用可卷动的结果集练习中采用的相对方法,或获取 检索的行数,然后使用绝对行和页面位置。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_HOME。在 NT 上,这是 J2EE SDK 的驱动 器字母和父目录,例如:

set J2EE_HOME=D:/j2ee

设置环境变量 JAVA_HOME。在 NT上,是 J2SE 的驱动器字母和 父目录,例如:

set JAVA_HOME=D:/j2se

请使用 J2EE 和 J2SE 目录自己的位置和名称。由此开始, J2EE_HOMEJAVA_HOME 都引用这些目录。

要避免类途径问题,请将下列 jars 从J2EE_HOME/lib/cloudscape 复制到并重命名为 JAVA_HOME/jre/lib/ext

 

  • cloudscape.jar
  • client.jar rename to cs_client.jar
  • RmiJdbc.jar rename to cs_RmiJdbc.jar
  • tools.jar rename to cs_tools.jar

 

不必重命名 jars (如果愿意可以使用不同的名称),但是这样做将避免任何 因为所用的常见姓名导致的冲突。可以随意进行各种设置,但是上述步骤不会更 改适当的客户机和服务器操作。注意,该设置会绕过 J2EE,并完全允许使用 Cloudscape。如果想结合本课程的练习使用 J2EE 部分,就必须修改 jGuru 所用的数据库名称或编辑练习程序和资源包,以使用默认的 CloudscapeDB 数据库。

Cloudscape 的更多信息,请查看 J2EE_HOME/doc/cloudscape/index.html 里 Cloudscape DBMS 的下载文档。不是所有文档都适用于 J2EE 下载中包 括的特别版本。因为本课程集中介绍标准,所以不详细讨论数据库细节,并且每 个数据库的设置都不同。这类信息和启动/关闭数据库的详情,请参考自己所用数 据库和驱动程序的文档。不过,会简要地介绍启动和关闭 Cloudscape。

启动和关闭 Cloudscape

 

确保已经安装 Cloudscape 并按 Cloudscape 安装和 设置中的介绍进行了设置。

必须使用命令行启动 Cloudscape。实际命令(请务必按照需要适当地替换 J2EE_HOME和斜杠或反斜杠)是:

J2EE_HOME/bin/cloudscape -start

对于正确地关闭 Cloudscape 的数据库完整性来说,尤其重要。除非绝对不可 避免,不要使用 CTRL-C 或其他的终止进程/终止方法。也必须从命令行关闭 Cloudscape。通常,这意味着开启另一个窗/外框,并键入下列命令(同样,请按 照需要适当替换 J2EE_HOME 和斜杠或反斜杠):

J2EE_HOME/bin/cloudscape -stop

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 语句。因为创建表操作非常 重要,所以需要最低的一致性。不过,有些数据来源,如 Text ODBC,仅仅支持 最简单的列元素,几乎没有约束支持。

CREATE TABLE