在
Java SE 6
所提供的诸多新特性和改进中,值得一提的是为
Java
程序提供数据库访问机制的
JDBC
版本升级到了
4.0,
这个以
JSR-221
为代号的版本
,
提供了更加便利的代码编写机制及柔性
,
并且支持更多的数据类型
.
在本文中,我们将从编码的易用性及柔性的角度探讨
JDBC 4.0
所带来的新特性及改进。
JDBC 4.0
的新特性
JDBC 4.0
文档列举了
20
个改进及新特性
,
大小不等
.
本文无法做到尽述其详
,
为此笔者根据其功能特点及应用领域将其分为下述四类:
1.
驱动及连接管理
2.
异常处理
3.
数据类型支持
4.
API
的变化
下面按照上述四类展开详述:
驱动及连接管理
驱动及连接的使用和结果集管理
—
在
JDBC
的很多地方都发生了显著的变化
连接数据库变得更加容易
如果您曾经有过
JDBC
开发的经验
,
那么我确信您还保存着一份建立数据库连接所必须的工作列表
.
而列表中的第一项即加载一个合适的驱动程序
.
您是否想过这个步骤应该被改进呢
?
在此版
JDBC
中做到了
.
您不必再显式地加载
Class.forName
了
.
当您的程序首次试图连接数据库时
,
DriverManager
自动加载驱动到当前应用的
CLASSPATH
.
这是
JDBC
的一个比较大的改动
.
尽管
DriverManager
现在可以自动地加载驱动
,
建立一个
DataSource
对象
仍是获取连接的推荐的方法
.
因为可以在配置中将数据源指向不同的数据库,
DataSource
更具透明性和灵活性。
这样就可以访问另一个数据库实例不需更改现有的任意一行代码
,
甚至数据库的驱动完全不同也没有关系
.
ResultSet
的使用变得更为灵活
ResultSet
接口的层次结构当前为编程的灵活性提供了一些新的机制
.
RowSet
子接口滚动、可提交并可离线编辑的
ResultSet
.
WebRowSet
子接口提供了从数据库表中获取数据
,
并将其序列化为
XML
文档
,
抑或是将
XML
解析成
result set
的能力
.
尽管上个版本的
JDBC
也提供了
RowSet
接口层次
,当前的版本对于
SQLXML
数据类型支持得更好
(
稍后讨论
)
,这些特性是的
JDBC
编程更加容易且具灵活性
.
提供了更多的
API
本版
JDBC
提供了更多的
API
以实现访问
SQL:2003
具备的新特性
.
此外
,
为了更好的操纵修改数据,
JDBC
也增添了许多的方法。
.
现在我们来看一些代码并讨论下面
Example1
类的输出结果
.
它将连接嵌入式的
Apache Derby
数据库并在控制台上显示输出结果
.
尽管
JDBC 4.0
已推出几个月了
,
笔者发现只有
Apache Derby
提供了支持
JDBC 4.0
规范的驱动
(
截至
2007
年
3
月
).
本文的所有例子均用
JDK 1.6
和
Apache Derby
数据库
10.2.2.0
开发
.
public class Example1 {
public static void main(String[] args) {
...
String dbName = "example1";
String tableName = "stu1";
ds = new EmbeddedDataSource40();
ds.setDatabaseName(dbName);
String connectionURL = "jdbc:derby:"+dbName+";create=true";
try {
con = ds.getConnection();
stmt = con.createStatement();
rs = stmt.executeQuery("select * from "+tableName);
int colCount= rs.getMetaData().getColumnCount();
for (int j=0; j< colCount; j++){
System.out.print(rs.getMetaData().getColumnName(j+1)
+ "\t");
}
while (rs.next()) {
System.out.print("\n");
for (int i = 0; i < colCount; i++) {
System.out.print(rs.getString(i + 1) + "\t");
}
}
} catch (SQLException e) {
e.printStackTrace();
}
finally{
//close connections
}
}
}
如果在
example1
数据库的
stu1
表中有数据的话
,
编译并运行
Example1.java
将在控制台获得以下输出:
ID NAME COURSE
1001 John Doe Statistics
1002 Jack McDonalds Linear Algebra
如果想看
DriverManager
如何自动加载
JDBC
驱动
,
可以将
Example1
中的:
con=ds.getConnection()
替换为:
con=DriverManager.getConnection(connectionURL)
.
该类将产生相同的输出。正如您所看到的,再也不用显式地调用
Class.forName()
了
.
异常处理
怎样辨别一个
Java
程序的健壮与否呢
?
在我看来,异常处理机制是重要的考虑因素之一
.
一个健壮的
Java
程序可以很好地处理异常
,
并给予程序在发生异常时恢复的能力
.
而一个不健壮的程序将导致输出错误的结果甚至导致整个应用的崩溃
!
JDBC 4.0
增加了一些简单而有力的异常处理机制
,
其中值得一提的是链式异常,如果这个异常链存在的话,即可应用增强了的
for
-
each
循环来获取异常链
,.
下面的
Example2
类的局部结构展示了如何应用这种新的方法处理链式异常:
public class Example2 {
public static void main(String[] args) {
String dbName = "example";
String tableName = "student4";
try {
con = ds.getConnection();
stmt = con.createStatement();
rs = stmt.executeQuery("select * from " + tableName);
} catch (SQLException sx) {
for(Throwable e : sx ) {
System.err.println("Error encountered: " + e);
}
}
finally{
//close connections
}
}
}
运行
Example2.java
,
注意
student4
并不是数据库中实际存在的表
.
将在下列调用中产生链式异常:
rs = stmt.executeQuery("select * from " + tableName);
在实际的应用中,需要捕捉到这些异常
,
检测并进行相应的处理
.
在本例中,笔者仅将其在控制台输出
.
以下是输出代码:
for(Throwable e : sx ) {
System.err.println("Error encountered: " + e);
}
以下是类
Example2
输出的结果
:
Error encountered: java.sql.SQLSyntaxErrorException:
Table/View 'STUDENT4' does not exist.
Error encountered: java.sql.SQLException:
Table/View 'STUDENT4' does not exist.
Exception in thread "main" java.lang.NullPointerException
at ex.Examlpe2.main(Examlpe2.java:51)
通过应用
JDBC 4.0,
您现在不需太多代码即可以获取及遍历异常链
.
在以往的版本中
,
您在遍历异常链时,必须手工的调用
getNextException
方法才能得到相同的效果
.
支持的数据类型
本版
JDBC
增加了一些新的数据类型,对其他的一些数据类型,则提供了更好的支持
.
笔者为
XML
被正式支持感到欣喜
,
本版中产生了一个新的接口
:
SQLXML
.
在笔者看来这个接口值得单独开一个章节为其讨论:
SQLXML
与
XML
的支持
SQLXML
是
SQL
中
XML
数据类型在
Java
中的表示,
XML
是
SQL
中用于表示表中
XML
数据的内建数据类型
.
在默认的情况下,
JDBC
驱动将
SQLXML
指针指向
XML
数据而不是数据本身
.
SQLXML
对象在其被创建的事务中是稳定的
.
在下面的
Example3
类中
,
笔者将说明如何在当前连接中应用
SQLXML
并更新表数据
.
public class Example3 {
public static void main(String[] args) {
...
con = ds.getConnection();
SQLXML sx= con.createSQLXML();
sx.setString("Math is Fun");
String psx ="insert into "+tableName+
" ( id, textbook) values(?,?) ";
PreparedStatement pstmt = con.prepareStatement(psx);
pstmt.setString(1,"1000");
pstmt.setSQLXML(2,sx);
pstmt.executeUpdate();
...
}
}
这个例子说明了您所能应用的最简单的情况
.
如果我们继续深入研究,事情就会变得有趣得多了
.
但在我们深入讨论之前
,
让我来告诉您运行
Example3.java
.
的结果。
非常不幸
,
我无法获取到
SQLXML
对象,并得到了以下让人失望的输出:
java.sql.SQLFeatureNotSupportedException: Feature not
implemented: no details.
at org.apache.derby.impl.jdbc.SQLExceptionFactory40.
getSQLException(Unknown Source)
... ... ... ...
at ex.Example3.main(Example3.java:62)
看来
Apache Derby
并没有提供从
Connection
中获取
SQLXML
对象的方法
.
但至少您可以看到笔者正试图在类
Example3
中实现的东西
:
我想插入一行新的数据:
id
列值为
1000
textbook
列
(S
QLXML
类型
)
插入
Math is Fun
.
笔者用如下代码段结束关于
SQLXML
的讨论,这段代码从数据库中读取
XML
值并将其转化为
Document
对象
.
SQLXML sqlxml = rs.getSQLXML(column);
InputStream binaryStream = sqlxml.getBinaryStream();
DocumentBuilder parser =
DocumentBuilderFactory.newInstance().newDocumentBuilder();
Document doc = parser.parse(binaryStream);
可以把一个列的值直接转化为
XML
文档不是一件令人兴奋的事情吗
?
我觉得这个特性非常好
.
ROWID
数据类型
SQL
ROWID
唯一标识了数据表中的一行,并是访问该行的最快的方法,
本版增加了
RowId
接口以提供对
ROWID
SQL
数据类型在
Java
类中的支持
.
对大对象类型支持的增强
JDBC
版本
2
提供了对大的
SQL
对象如:
CLOB
,
BLOB
,
ARRAY
的支持
,
及用于添加相关接口的
Struct:
Clob
,
Blob
,
Array
, and
Struct
.
在本版的
JDBC
中增加了很多对这些对象访问的新方法
.
笔者将在
API
变化一节中进行详细论述
.
支持
National Character Set
(
NCS
)
转化
SQL:2003
提出了如下
SQL
数据类型的支持:
NCHAR
,
NVARCHAR
,
LONGNVARCHAR
,
及
NCLOB
.
其功用和
CHAR
,
VARCHAR
,
LONGVARCHAR
,
及
CLOB
类似
,其区别仅是,这些类型的文本是用
NCS
编码的。
如果需要大量的字符处理,您可能更倾向于
NCS
数据类型而非普通的数据类型。本版
JDBC
提供了增强对
NCS
支持的
API.
-
在
PreparedStatement
,
CallableStatement
,
及
ResultSet
接口中增加了一些
setter
和
updater
方法以支持
NCS
转化
.
比如方法
setNString
,
setNCharacterStream
,
及
setNClob
等等
.
-
在
SQLInput
and
SQLOutput
接口中增加了读写方法以支持
t
NClob
和
NString
对象
.
API
变化
JDBC 4.0
最大的变化来自于
API,
笔者在本小节对其做简单介绍
.
Array
Array
接口增加了一个
free
方法来释放
array
对象及其持有的资源
.
Connection
和
PooledConnection
Connection
接口现在提供一系列创建大对象的方法如
createClob
,
createBlob
等等
.
此外还有
getter
和
setter
对客户端信息的重载方法
,
及验证当前连接正确性的方法
.
PooledConnection
接口当前提供
addStatementEventListener
和
removeStatementEventListener
两个方法来注册和注销
StatementEventListener
接口
,
这个接口是在本版
JDBC
中新引入的
.
这个接口的一个实例将获取到
S
tatement
池中
PreparedStatement
s
的变化
.
比如,在注册以后
,
当驱动调用
statementClosed
方法时,所有
StatementEventListener
将获得
statement
已关闭的通知
.
DatabaseMetaData
不同的关系数据库往往支持不同的特性
,
并通过不同的方法来实现这些特性
,
并可能会是用不同的数据类型
.
这将会导致移植性的问题,因为根据实现的不同,无法保证代码在所有关系数据库上都能正确执行
.
这样的问题在一定程度上可以通过这个接口所获得的信息来解决
.
比如,如果您在写一个通过传入
SQL
语句来建立表的代码
.
您可能想知道在
CREATE TABLE
语句中有哪些数据类型是可用的,此时可以调用该接口中的
getTypeInfo
方法
.
本版
JDBC
增加了一些获取信息的方法
.
在
Example4
中
,
我将通过一段代码展示如何获得满足某种模式的数据库结构的列表。
.
con = ds.getConnection();
DatabaseMetaData dmd = con.getMetaData();
rs=dmd.getSchemas("TABLE_CAT", "SYS%");
//iterate over the rs and print to console
首先通过调用
dmd.getCatalogs
并遍历结果集
,
得到了唯一的一个值:
TABLE_CAT
.
接着通过调用
rs=dmd.getSchemas("TABLE_CAT", "SYS%")
得到以
SYS
开头的数据库和表结构
.
以下是笔者得到的结果
:
SYS
SYSCAT
SYSCS_DIAG
SYSCS_UTIL
SYSFUN
SYSIBM
SYSPROC
SYSSTAT
Scalar
函数支持
一个
scalar
函数操作预定义的输入数据集合并返回结果
.
比如:
scalar
函数调用
ABS(number)
返回
number
的绝对值
.
这些
scalar
函数可以作为
SQL
字符串的一部分来使用
.
本版
JDBC
要求当所依赖的关系数据库支持以下功能时:
CHAR_LENGTH
,
CHARACTER_LENGTH
,
CURRENT_DATE
,
CURRENT_TIME
,
CURRENT_TIMESTAMP
,
EXTRACT
,
OCTET_LENGTH
,
和
POSITION
,驱动必须实现这些功能。
Statement
,
PreparedStatement
,
和
CallableStatement
Statement
接口当前提供
isClosed
方法来判断
statement
是否已关闭
,
setPoolable
用来设置是否可以被池化
,
用
isPoolable
来检测当前的池化状态。
PreparedStatement
及
CallableStatement
接口现在提供了更多插入大对象的方法
,
通过使用
InputStream
及
Reader
等
.
Wrapper
这个版本的
API
增加了一个新的
Wrapper
接口,
来提供一种访问资源的实例的方法
,
这可能是基于架构的考虑
. Wrapper
模式
,
被许多的
JDBC
驱动实现应用以提供
JDBC API
之外的依赖于具体数据源的应用
.
这个接口的主要目的是用来提供供应商相关的功能。您可以通过调用
unwrap
方法来获取到数据库连接的接口实现的实例
.
因为这是一个重量级的操作
,
在使用前,应该先调用
isWrapperFor
方法来检测是否当前实例是某种实现的一个间接或直接的
Wapper
能够给出一个程序例子当然是最好的,但是
Apache Derby
参考手册
l
指出
: "JDBC 4.0
引入了
wrapped JDBC
对象的概念
...
对于
Derby
来说
,
这对
Derby
来说是没有意义的,因为
Derby
并不做规范之外的扩展
."
因此看来这种尝试也就变得无甚必要了
!
结论
我们已经分为
4
类讨论了
JDBC 4.0
所做的一些改进和新的特性,这些新特性增加了编程易用性,提高了生产率
.
尽管
API
规范已经推出几个月了
,
到笔者截稿时,主流的数据库厂商都没有提供本版的
JDBC
驱动
.
当更多的供应商开始支持
JDBC 4.0
时
—
当然也包括您所中意的那个
—
您就可以享受
JDBC4.0
所提供的这些易用的功能了
.
最后,我认为有一个各大数据库厂商的支持的
JDBC
版本的列表是必要的
. Sun Developer Network (SDN)
上有一个
JDBC Data Access API
(
http://developers.sun.com/product/jdbc/drivers
)
页提供了一份更新不太及时的列表
.
原作者信息:
Sharad Acharya has more than eight years of experience in the software engineering field in multiple business domains including supply chain, insurance, banking, and mortgage.
@2008 杨一. 版权所有. 保留所有权利