清华大学计算机系 吕思飞 香港中文大学计算机系 姚剑
随着Java语言应用面的逐步拓宽,Sun Microsystems 公司开发了一个标准的SQL数据库访问界面———JDBC API。它可以使Java编程人员通过一个一致的界面,访问多种关系数据库。

JDBC API定义了一系列Java类,用来表示数据库连接、SQL语句、结果集、数据库元数据等,能够使Java编程人员发送SQL语句和处理返回结果

JDBC API由一个驱动程序管理器实现对连接到不同数据库的多个驱动程序的管理。

JDBC驱动程序可以全部由Java语言编写,也可以由本地化方法来实现与现有数据库访问接口的连接。

 

 一、总体结构

 Java应用程序通过JDBC API界面访问JDBC管理器,

JDBC管理器通过JDBC驱动程序API访问不同的JDBC驱动程序,

从而实现对不同数据库的访问。

图1表示JDBC总体结构。

1.JDBC API

JDBC API定义了一系列抽象Java界面,可以使应用程序员连接到指定的数据库,执行SQL语句和处理返回结果。

功能结构如图2所示。 

JDBC API中重要的界面有:

java.sgl.DriverManager:完成驱动程序的装载和建立新的数据库连接。

java.sgl.Connection:表示对某一指定数据库的连接。

java.sgl.Statement:管理在一指定数据库连接上的SQL语句的执行。

java.sgl.ResultSet:访问一指定语句的原始结果。

java.sgl.Statement:界面有两个重要的子类型:ja-va.sgl.PreparedStatement (用于对预编译的SQL语句的执行)和java.sgl.CallableStatement(用于对一个数据库存储过程的执行)。

2. JDBC驱动程序界面

数据库驱动程序中必须实现在JDBC API中定义的抽象类,尤其是对java.sgl.Connection、java.sgl.Prepared-Statement、java.sgl.CallableStatement和java.sgl.ResultSet的实现。

此外,每一个数据库驱动程序必须提供一个类实现

java.sgl.Driver界面,用于通用的java.sgl.DriverManager类,使其在对一个指定的数据库URL访问时可以查找相应的驱动程序。

Sun Microsystems公司提供了一个JDBC对ODBC的连接桥,如图1中 JDBC-ODBC桥接驱动程序所示。由于是建立在ODBC基础之上,所以具有规模较小而效率高的特点。

3.用户典型使用模式

(1)Applet模式

使用Java语言编制 Applet,作为 WWW文档的一部分在Internet上发布,由浏览器对其下载,并运行。

其中,可能会有部分Applet需要访问数据库,则可通过使用JDBC API实现,其应用如图3所示。

图3 Applet应用模式

由于未信任的Applet不能访问本地文件和对任意主机建立网络连接,JDBC必须遵守标准Applet的安全模型,所以此种应用模式有诸多安全方面的限制

对信任的Applet,其安全模型类似于Java应用程序。

(2)应用程序模式

在应用程序中,Java代码是信任的,因而可以读写本地文件和建立网络连接。应用示意如图4所示。

此种应用模式非常适合intranet(内部网)应用。

当然该模式也能够通过Internet访问数据库。

 

二、JDBC API

 

1.JDBC界面定义

 

JDBC API包含java.sgl的界面和类

java.sgl.CallableStatement:用于执行存储的SQL过程的界面。

java.sgl.Connection: 一个连接表示与某一指定数据库的一个会话。在该连接中可以执行SQL语句和处理返回结果。

java.sql.DataTruncation:当JDBC碰到意外数据截断时,报告一个警告(读数据时)或产生一个异常(写数据时)。

java.sql.Date:是标准java.util.date的一个子集,只表示天数,而不包含时、分、秒。

java.sql.Driver:定义一个在每一个数据库驱动程序中必须实现的驱动程序界面。 java.sql.DriverManager:提供对全局SQL状态的访问。

java.sql.DriverPropertyInto:提供高级程序员与驱 动程序之间对连接特性信息进行交互的手段。 java.sql.NullData:当由getXXX或getObiect方法读出一个SQL空值时,产生一个NullData警告。 java.sql.Numeric:是一个任意精度标量数值类,可用作表示SQL定点Numerlc和Decimal类型的数值。 java.sql.PreparedStatement:保存一个预编译的SQL语句的对象,该对象可被高效地执行多次。 java.sql.ResultSet:结果集提供对执行一个SQL语句后产生的结果表的访问。表中数据按行依次取出。为便于移植,建议对每一行数据从左 至右按列读出。

java.sql.SQLException:处理数据库访问时的出错信息。

java.sql.SQLWarning:处理数据库访问时的警告信息。

java.sql.Statement:用作执行一条静态的SQL语句并接收产生的结果。

java.sql.Time:用于表示标准java.util.date类的一个信息子集,仅表示时、分、秒。 java.sql.Timestamp:扩展标准java.util.date类,使其能够表示SQL的时间戳,增加了一个以纳秒为单位的时间域。

java.sql.Types:定义区分SQL类型的常量。类常量值与XOPEN中的值相同。

此外,JDBC API 还定义了JDBC元数据界面java.sql.DatabaseMetaData 和java.sql.ResultSetMetaData。

图5 界面之间的关系

界面间的关系由图5表示,其中箭头表示函数,而线表示其它方法。

 

2.数据库连接

(1)建立一个连接

用户在访问数据库时,需要在JDBC管理层由 java.sql.DriverManager.getConnection方法产生一个java.sql.Connection对象

该方法使用一个数据库URL串作为参数。

(2)选择合适的驱动程序

在数据库URL中,可以指定驱动程序的名称,也可以不指定。

如果不指定驱动程序,则从Java特性"sql.drivers"所指出的驱动程序表中依次搜寻,使用最先找到的可成功连接的驱动程序。

(3)数据库URL

在连接时,由数据库URL参数指定要连接的数据库,此时可称为JDBC URL,其格式为:

 

jdbc:<子协议>:<子名称>

 

如果是对网络数据库访问,那么建议用户使用标准URL作为子名称的一部分。

比如对数据资源名为fred访问的URL可能是: jdbc:odbc:fred 或jdbc:dbnet://wombat:356/fred

子协议odbc表示对ODBC数据资源的访问,其格式为: jdbc:odbc:<数据资源名>[;<属性名>=<属性值>]*

  ·连接参数:

java.util.Properties对象指出。

建议大多数参数不要在此处给出,而在协议中指出。

  ·支持多连接:

一个应用程序可以使用一个或多个驱动程序建立与多个数据库连接。

  ·驱动程序的注册:

有两种方法,

一是在JDBC java.sql.DriverManager类初始化时查找"sql.drivers"特性,对每一个驱动程序自动注册;

二是由标准 Class.forName方法显式加载一个驱动程序,参数为驱动程序名。

 

3.参数传递和结果接收

(1)查询结果 执行一条查询语句后,返回结果是可由java.sql.ResultSet对象访问的行的集合

在该对象中提供了一系列"get" 方法,访问当前的每一列,Result-Set.next方法可实现在结果集的行之间移动,可以使用列索引或列名指定相应的列。

·查询结果的数据转换

。 ResultSet.getXXX方法可以把SQL类型转化为需要的Java类型

。若指定一个非法的类型转换时,则产生一个SQLException的异常

·空值判断

。先读出某一列数据,然后使用Result-Set.wasNull方法,判断返回结果是否是SQL"NULL"

·长数据的读出

。JDBC支持由getByte和getString 方法读出任意长的LONGVARBINARY或LONGVAR-CHAR类型数据,也支持由方法GetBinaryStream、 GetAsciiStream 和 GetUnicodeStream返回数据流来读出数据

·支持用getResultSet、GetUpdateCount 和 Get-MoreResults方法分别返回一条结果、返回被修改的行数和返回多条结果。

 

(2)传递IN参数

java.sql.PreparedStatement界面提供了一系列setXXX方法向SQL语句传递参数,实现动态的SQL语句。

在传递参数时必须满足数据类型一致的要求。

因此必须预先调用类型转换方法完成数据转换,同时也提供了传递SQL空值和长数据给IN参数的方法。

 

(3)接收OUT参数

在调用一个存储过程时,可用setXXX方法传递IN参数,使用OUT参数接收返回结果。

在使用时必须先调用CallableStatement.registerOutParameter方法为每一个OUT参数进行类型注册,然后执行该过程调用语句,最后使用getXXX方法取出OUT参数的结果。

返回结果的数据类型是与用户注册的SQL类型相对应的Java类型。

空值的处理步骤是,先读出参数值,再用CallableStatement.wasNull方法判断是否为空值。
不支持以流形式读出OUT参数的机制。

接收时返回结果的顺序优先于OUT参数。

 

(4)数据截断

在某种条件下,有可能在读或写数据时出现数据截断,如当由Connection.setMaxFieldSize设置了一个域的最大长度时,超过设置长度后的数据就被截断。

在读数据时,如出现数据截断,则产生一条DataTruncation的 SQLWarning警告。

在写数据时,如发生数据截断,则产生一个DataTruncation的SQLException异常。

 

4. SQL数据类型到Java类型的转换

由于SQL数据类型与Java数据类型之间差异较大,可相互转换的类型之间还是存在一些不一致的地方,因此JDBC提供了详细的从SQL类型到Java类型的标准转换表从Java类型到SQL类型的标准转换表

 

 

三、进一步了解JDBC API

 

1.异步、线程和交易

 

(1)异步请求

某些数据库API (比如ODBC) 提供了SQL语句异步执行的机制,这样可使一个数据库的操作在后台运行的同时,前台一边等待一边处理其它操作。

由于Java提供多线程机制,因此并不真正需要实现异步SQL语句的执行。当需要异步执行时,可通过创建一个新线程来实现数据库操作。

 

(2)多线程

对java.sql的所有对象的操作是多线程安全的,并且当多个线程同时访问一个对象时,也保证操作的正确性。

尽管不同的驱动程序其并发执行程度可能不同,但开发人员可以假定为完全并发执行的。

因为驱动程序若需要某种形式的同步操作,则一定会提供相应的实现机制。

另一个多线程的特殊应用是可以取消一个执行时间过长的语句操作。具体做法是启动另一个线程调用Statement.cancel()方法

(3)交易

每一个新的JDBC连接都初始化为"自动提交"模式,即意味着每一条语句作为一个分开的交易来执行

当需要把多条语句作为一个完整的交易来执行时,可以调用Connection.setAutoCommit(false)方法,取消自动提交。执行完一个交易后,调用Connection.Commit显式完成提交,或调用Connection.rollback卷回整个交易操作。

当一个交易被提交或卷回后,关闭所有在此连接上的PreparedStatements、CallableStatements和ResultSets,只有简单的Statements是打开状态。

 

2.指针

 

JDBC支持简单指针,这里说的指针是指SQL数据库中的概念

应用程序可以用ResultSet.GetCursorName()方法,取得与当前Resultset相关联的指针,利用该指针可以对当前行进行修改和删除。

指针的有效期是到ResultSet或其父语句结束。

 

3.对SQL的扩充

 

JDBC全部支持SQL-2基本(Entry)级规范,部分支持SQL-2过渡(Transitional)级规范。对SQL-2基本级扩充有二点:

一是支持DROP TABLE命令;

二是选定的过渡级语义必须通过Escape语法来支持,以便一个驱动程序可以方便地扫描和翻译成特定DBMS语法。

 

(1) SQL Escape语法

存储过程、标量函数、日期、时间、输出连接等方面,JDBC支持与ODBC相同的DBMS无关的Escape转义语法,格式为:

{关键字……参数……}

 

(2)存储过程

JDBC中激活一个存储过程语法格式是:

{ call 过程名[参数1,参数2,…] }

或者是带返回结果参数的过程: { ? = call 过程名[参数1,参数2,…] }

 

(3)时间和时期文字量

JDBC支持这些文字量的ISO标准格式,用Escape转义的语句表示时间和日期,如{ d 'yyyy-mm-dd'}或{ t 'nn:mm:ss'} 分别表示日期或时间。

 

(4)标量函数

JDBC支持标量值的数值、串、时间、日期、系统和转换函数,如,{ fn concat ("Hot", "Java") }。

 

(5)输出连接

语法格式为:

{ oj outer-join }

其中outer-join形式为: table LEFT OUTER JOIN { table( outer-join} ON search-condition

 

4.动态数据库访问

 

尽管我们希望JDBC程序员在编程时能够了解数据库的模式,以便使用强类型的JDBC界面对数据库进行访问

但一个应用程序,有时需要动态得到数据库模式,并以此信息完成相应的动态数据库访问。

 

(1)元数据信息

JDBC能够对元数据进行访问,比如行结果描述、语句参数、数据库特性等。 Java.Sql.ResultSetMetaData类型提供了大量方法去获得一个指定 Java.sql.ResultSet对象列的类型和特性。 Java.Sql.DatabaseMetaData界面提供一系列方法去获得与某一数据库相关联的各种元数据,包含数据库的过程、表、模式等和表中的列、列的访问权、表的访问权等信息。

 

(2)动态类型数据访问

JDBC使用ResultSet.getObject、PreparedStatement.setObject和CallableStatement.getObject方法支持更一般的数据访问。

动态访问时从SQL类型到Java对象类型的转换表和从Java对象类型到SQL类型的转换表在此就不加以列举了。但上述三个方法中类型转换的依据是这两张表。

 

四、结束语

 

Java语言提供了访问数据库的API,这非常有助于其拓宽应用范围。由于目前仅实现了一个基础的API,所以希望开发人员以此为基础,建立更高级的类和应用工具,同时,也希望诸多开发商提供多种数据库驱动程序,以满足编程人员访问不同数据库资源的需要。