关于 JDBC 您可能不知道的 5 件事
JDBC,即 Java? Database Connectivity 是 JDK 中最常用的包之一,但是只有极少数开发人员能够充分使用其完整的 — 或最新的 — 功能。Ted Neward 提供了像 ResultSet 这样的最新 JDBC 功能,可以在系统繁忙时自动滚动和更新,无论是否有开放数据库连接,Rowset 都能正常工作,而且批量更新可围绕网络快速执行多条 SQL 语句。
目前,许多开发人员把 Java Database Connectivity (JDBC) API 当作一种数据访问平台,比如 Hibernate 或 SpringMany。然而 JDBC 在数据库连接中不仅仅充当后台角色。对于 JDBC,您了解的越多,您的 RDBMS 交互效率就越高。
本文将向您介绍几种 JDBC 2.0 到 JDBC 4.0 中新引入的功能。设计时考虑到现代软件开发所面临的挑战,这些新特性支持应用程序可伸缩性,并提高开发人员的工作效率 — 这是现代 Java 开发人员面临的两个最常见的挑战。
1. 标量函数
不同的 RDBMS 实现对 SQL 和/或增值特性(目的是让程序员的工作更为简单)提供不规则的支持。例如,众所周知,SQL 支持一个标量运算 COUNT(),返回满足特定 SQL 过滤规则的行数(更确切地说,是 WHERE 谓词)。除此之外,修改 SQL 返回的值是很棘手的 — 想要从数据库获取当前日期和时间会使 JDBC 开发人员、甚至最有耐心的程序员发疯(甚至是心力憔悴)。
于是,JDBC 规范针对不同的 RDBMS 实现通过标量函数提供一定程度的隔离/改写。JDBC 规范包括一系列受支持的操作,JDBC 驱动程序应该根据特定数据库实现的需要进行识别和改写。因此,对于一个支持返回当前日期和/或时间的数据库,时间查询应当如清单 1 那样简单:
清单 1. 当前时间?
Connection conn = ...; // get it from someplace
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery("{CURRENT_DATE()}");
JDBC API 识别的标量函数完整列表在 JDBC 规范附录中给出,但是给定的驱动程序或数据库可能不支持完整列表。您可以使用从Connection 返回的 DatabaseMetaData 对象来获取给定 JDBC 支持的函数,如清单 2 所示:
清单 2. 能为我提供什么?
Connection conn = ...; // get it from someplace
DatabaseMetaData dbmd = conn.getMetaData();
标量函数列表是从各种 DatabaseMetaData 方法返回的一个逗号分隔的 String。例如,所有数值标量由 getNumericFunctions() 调用列出,在结果上执行一个 String.split() — 瞧! — 即刻出现 equals()-testable 列表。
2. 可滚动 ResultSets
创建一个 Connection 对象,并用它来创建一个 Statement,这在 JDBC 中是最常用的。提供给 SQL SELECT 的 Statement 返回一个ResultSet。然后,通过一个 while 循环(和 Iterator 没什么不同)得到 ResultSet,直到 ResultSet 为空,循环体从左到右的每次提取一列 。
这整个操作过程是如此普遍,近乎神圣:它这样做只是因为它应该这样做。唉!实际上这是完全没必要的。
引入可滚动 ResultSet
许多开发人员没有意识到,在过去的几年中 JBDC 已经有了相当大的增强,尽管这些增强在新版本中已经有所反映。 第一次重大增强是在 JDBC 2.0 中,发生在使用 JDK 1.2 期间。写这篇文章时,JDBC 已经发展到了 JDBC 4.0。
JDBC 2.0 中一个有趣的增强(尽管常常被忽略)是 ResultSet 的滚动功能,这意味着您可以根据需要前进或者后退,或者两者均可。这样做需要一点前瞻性,然而 — JDBC 调用必须指出在创建 Statement 时需要一个可以滚动的 ResultSet。
如果底层 JDBC 驱动程序支持滚动,一个可滚动的 ResultSet 将从那个 Statement 返回。但是在请求它之前最好弄清楚驱动程序是否支持可滚动性。您可以通过 DatabaseMetaData 对象探询滚动性,如上所述,这个对象可从任何 Connection 中获取。
一旦您有了一个 DatabaseMetaData 对象,一个对 getJDBCMajorVersion() 的调用将会确定驱动程序是否支持 JDBC 规范,至少是 JDBC 2.0 规范。当然一个驱动程序可能会隐瞒它对给定规范的支持程度,因此为了安全起见,用期望得到的 ResultSet 类型调用supportsResultSetType() 方法。(在 ResultSet 类上它是一个常量;稍后我们将对其每个值进行讨论。)
清单 3. 可以滚动?
int JDBCVersion = dbmd.getJDBCMajorVersion();
boolean srs = dbmd.supportsResultSetType(ResultSet.TYPE_SCROLL_INSENSITIVE);
if (JDBCVersion > 2 || srs == true)
{
// scroll, baby, scroll!
}
请求一个可滚动的 ResultSet
假设您的驱动程序回答 “是”(如果不是,您需要一个新的驱动程序或数据库),您可以通过传递两个参数到Connection.createStatement() 调用来请求一个可滚动的 ResultSet,如清单 4 所示:
清单 4. 我想要滚动!
Statement stmt = con.createStatement(
ResultSet.TYPE_SCROLL_INSENSITIVE,
ResultSet.CONCUR_READ_ONLY);
ResultSet scrollingRS = stmt.executeQuery("SELECT * FROM whatever");
在调用 createStatement() 时,您必须特别小心,因为它的第一个和第二个参数都是 int 的。(在 Java 5 之前我们不能使用枚举类型!)任何 int 值(包括错误的常量)对 createStatement() 都有效。
第一个参数,指定 ResultSet 中期望得到的 “可滚动性”,应该是以下 3 个值之一:
ResultSet.TYPE_FORWARD_ONLY