📄 jdbc-spec.frame15.html
字号:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
<html>
<head>
<meta HTTP-EQUIV="Content-Type" CONTENT="text/html; CHARSET=gb2312">
<title></title>
</head>
<body bgcolor="#ffffff">
<table width="600">
<tr>
<td><font size="-1"><a href="jdbc-spec.frame.html">目录</a> | <a
href="jdbc-spec.frame14.html">上一页</a> | <a href="jdbc-spec.frame16.html">下一页</a>
</font></td>
<td align="right"><i>JDBC<sup><font size="-2">TM</font></sup> 指南:入门</i></td>
</tr>
</table>
<hr>
<p><br>
<a name="3883"></a> </p>
<h2>附录 A: 放弃的设计方案</h2>
<h3>A.1 使用 Holder 类型,而非 get/set 方法。</h3>
<p>在 JDBC 的初稿中,我们用“ Holder
”类型机制传递参数、获取结果。这种机制是试图在 ODBC
中模拟变量指针的用法。但当我们编写测试示例程序时发现:该机制需要创建和捆绑
Holder 类型,而这是很烦琐的,尤其是处理行结果时。</p>
<p><a name="4118"></a>于是我们提出了另一种设计方案,即 getXXX 和 setXXX
方法,这两种方法在<a href="jdbc-spec.frame7.html#20240">第 7.2 节</a>和<a
href="jdbc-spec.frame7.html#4149">第 7.1 节</a>中有具体说明。在比较了各种示例程序后,我们得出的结论是:对程序员来说,getXXX/setXXX
机制使用起来更简单些。它也使人们不用再为 JDBC API 定义成打的
Holder 类型。于是我们决定不再用 Holders 而改用 getXXX/setXXX 机制。</p>
<p><a name="4026"></a> </p>
<h4>A.1.1 用 Holder 类型传递参数</h4>
<p>作为 java.sql API 的一部分,我们定义了 Holder 类型集来存放 SQL
语句的参数。有了抽象基本类 Holder,也就有了特定的、不同 Java
类型且与 SQL 一起使用的子类型。例如,StringHolder
将保存字符串参数,而 ByteHolder 将保存字节参数。</p>
<p><a name="3897"></a>为使参数能传给 SQL 语句,java.sql.Statement 类允许将
Holder 对象与某一参数关联起来。执行语句时,将从相应的 Holder
对象读出 IN 或 INOUT 参数值;语句结束时,OUT 或 INOUT
参数将被写回相应的 Holder 对象。</p>
<p><a name="3898"></a>下面是 Holders 用于 IN 参数的示例: </p>
<pre><code>java.sql.Statement stmt = conn.createStatement();
</code></pre>
<pre><code>// 传递两个参数,其中一个在 for 循环中每次都改变,
</code></pre>
<pre><code>// 而另一个则保持不变。
</code></pre>
<pre><code>IntHolder ih = new IntHolder();
</code></pre>
<pre><code>stmt.bindParameter(1, ih);
</code></pre>
<pre><code>StringHolder sh = new StringHolder();
</code></pre>
<pre><code>stmt.bindParameter(2, sh);
</code></pre>
<pre><code>sh.value ="Hi"
</code></pre>
<pre><code>for (int i = 0; i < 10; i++) {
</code></pre>
<pre><code> ih.value = i;
</code></pre>
<pre><code> stmt.executeUpdate("UPDATE Table2 set a = ? WHERE b = ?");
</code></pre>
<pre><code>}
</code></pre>
<p>下面是 Holder 用于 OUT 参数的示例: </p>
<pre><code>java.sql.Statement stmt = conn.createStatement();
</code></pre>
<pre><code>IntHolder ih = new IntHolder();
</code></pre>
<pre><code>stmt.bindParameter(1, ih);
</code></pre>
<pre><code>StringHolder sh = new StringHolder();
</code></pre>
<pre><code>stmt.bindParameter(2, sh);
</code></pre>
<pre><code>for (int i = 0; i < 10; i++) {
</code></pre>
<pre><code> stmt.executeUpdate("{CALL testProcedure(?, ?)}");
</code></pre>
<pre><code> byte x = ih.value;
</code></pre>
<pre><code> String s = sh.value;
</code></pre>
<pre><code>}
</code></pre>
<h4>A.1.2 <strong>用 Holder 对象获取行结果</strong></h4>
<p>在执行语句前,应用程序员可以将 Holder
对象和某些列捆绑在一起。语句执行之后,应用程序可以用
ResultSet.next() 重复 ResultSet 以移到下一行。在应用程序移到某行时,Holder
对象将和该行的值一起 移动。这跟 ODBC 中用的 SQLBindColumn
机制相似。</p>
<p><a name="4004"></a> 下面是一个简单示例: </p>
<pre><code>// 执行一条 SQL 语句,返回
</code></pre>
<pre><code>// 行的集合,其中第一列是 int 类型,第二列是
</code></pre>
<pre><code>// String 类型,第三列是字节数组。
</code></pre>
<pre><code>java.sql.Statement stmt = conn.createStatement();
</code></pre>
<pre><code>IntHolder ih = new IntHolder();
</code></pre>
<pre><code>stmt.bindHolder(1, ih);
</code></pre>
<pre><code>StringHolder sh = new StringHolder();
</code></pre>
<pre><code>stmt.bindHolder(2, sh);
</code></pre>
<pre><code>BytesHolder bh = new BytesHolder();
</code></pre>
<pre><code>stmt.bindHolder(3, bh);
</code></pre>
<pre><code>ResultSet r = stmt.executeQuery("SELECT a, b, c FROM Table7");
</code></pre>
<pre><code>while (r.next()) {
</code></pre>
<pre><code> // 打印输出当前行的值。
</code></pre>
<pre><code> int i = ih.value;
</code></pre>
<pre><code> String s = sh.value;
</code></pre>
<pre><code> byte b[] = bh.value;
</code></pre>
<pre><code> System.out.println("ROW = " + i + " " + s + " " + b[0]);
</code></pre>
<pre><code>}
</code></pre>
<pre><code>
</code></pre>
<h3>A.2 候选设计:不用 fooHolder 类型,而用 foo[ ]</h3>
<strong>
<p>将来我们可能会支持列形式的捆绑,这样就能同时读取行块。在使用
Holder 的同时,我们也考虑了下面一种</strong> <strong>允许以列形式进行捆绑的新设计。</strong></p>
<p><a name="3262"></a>Holder 对象能够保存各种 Java
类型的单个实例。但单元素数组也能用作 Holder。这种方法除了一个明显优势之外也有些缺点。</p>
<p><a name="3264"></a>缺点之一:读“foo f[] = new foo[1];”时感到含混。相应的
Holder 声明“fooHolder f = new fooHolder();”却可以对 f
是什么及为什么要分配它给出较为明确的提示。</p>
<p>缺点之二:对于每个数组类型,必须用不同的方法取代单个方法
Statement.bindColumn。这是由于所有 Holder 类型都是从 java.sql.Holder
继承而来的,所以它们可作为参数传给使用 java.sql.Holder
参数的通用方法(另一方面,至少避免了定义成打的 holder 类)。</p>
<p><a name="3266"></a>最后一个缺点:使用 foo[] 只能提供原始 Java
类型信息。通过定义与 SQL 一起使用的 Holder
类型集,我们却能为诸如 CurrencyHolder 类型定义更多的域和/或语义。</p>
<p><a name="3267"></a>相应的明显优势是:如果我们把 foo[1]
看成是参数的容器,自然可将 foo[x]
视作一种在列形式捆绑中捆绑表中多行的途径。这可在不修改接口的情况下即能支持列形式的捆绑。</p>
<p>如果用数组代替 Holders,那么 bindColumn
机制将使比例变换到列形式的捆绑更加易于进行。</p>
<p><a name="17410"></a> </p>
<h3>A.3 支持一次检索多行</h3>
<p>目前我们提供的方法是在单行中检索单列,即每次一个域。我们期望驱动程序正常情况下能预取得更大的行块,从而减少与目标数据库的交互量。但它也可能有助于程序员通过
JDBC API 以更大块的方式检索数据。</p>
<p><a name="17412"></a>Java
中最简单的支持机制可能就是支持一些列形式的捆绑,其中程序员可以指定用作保存下面每一列的
20 个值的数组集,然后一次读取 20 行。</p>
<p>但我们不打算在 JDBC
的第一个版本中提供这种机制。我们建议一般情况下驱动程序能预取得大小适中的行块。</p>
<p><a name="20127"></a> </p>
<h3>A.4 ResultSet.get 方法中的列号</h3>
<p>JDBC 规范的早些版本中,各种“get”方法均不使用参数,而只按从左到右的顺序返回下一列的值。我们引入
列号参数的原因就是因为对最终实例代码的可读性不太满意。我们经常发现,为将它们与
SELECT 语句指定的 列匹配,需要用各种“get”调用进行计数。</p>
<p><a name="20141"></a> </p>
<h3>A.5 set 方法的方法重载</h3>
<p>在早期版本中,我们使用方法重载而不必使方法冠以各式各样的名字(如
setByte、setBoolean 等),所有 这些方法简称为 setParameter,并且只通过不同的参数类型加以区别。虽然这在
Java 中是合法的,但评论 家却说它令人费解且易于出错,尤其是在
SQL 类型和 Java
类型之间映射不明确的情况下。经考虑后,我们同意了他们的意见。</p>
<p><a name="20152"></a> </p>
<h3>A.6 避开 registerOutParameter 方法</h3>
<p>我们讨厌使用 registerOutParameter 方法。在开发 JDBC
期间,我们努力避开它,方法是让驱动程序用数据库元数据来确定
OUT
参数类型。但评论员的意见使我们相信,由于性能方面的原因,用
registerOutParameter 指定 OUT 参数类型会更合适。</p>
<p><a name="20172"></a> </p>
<h3>A.7 对大 OUT 参数的支持。</h3>
<p>目前我们不支持太大的 OUT 参数。如果我们要为非常大的 OUT
参数提供机制,就可能包括允许程序员注册 java.io.OutputStreams(当语句执行时,JDBC
运行时可将 OUT 参数的数据发送到其中)。但鉴于已经有了
一种将大结果作为 ResultSet
组成部分进行处理的机制,因此比起其实际价值来说解释可能要更难一些。</p>
<p><a name="20169"></a> </p>
<h3>A.8 支持 GetObject/getXXX</h3>
<p>由于各种 get/set 方法和通用 getObject/setObject
方法之间有重叠,因此我们考虑放弃 get/set 方法而只用
getObject/setObject。但如果程序员知道 SQL
类型,则最终的强制类型转换和提取就显得极其烦琐: </p>
<p><a name="24866"></a> int i = ((Integer)r.getObject(1,
java.sql.Types.INTEGER)).intValue() </p>
<p><a name="24863"></a>于是我们决定对此作出一点让步,即保留各种
get/set 方法作为大多数应用程序员的首选接口,但同时也增加
getObject/setObject 方法以备工具构造器和复杂应用程序使用。</p>
<p><a name="25317"></a> </p>
<h3>A.9 isNull/wasNull</h3>
<p>在决定处理 SQL NULL
的方法中,我们遇到了一些困难。不过,我们还是用 JDBC 0.50 设计了
ResultSet.isNull
方法,而且用起来也似乎颇为合意。读列之前(或之后)都可以在列中调用
isNull 方法以检查是否为 NULL。</p>
<pre><code>if (!ResultSet(isNull(3)) {
</code></pre>
<pre><code> count += ResultSet.getInt(3);
</code></pre>
<pre><code>}
</code></pre>
<p>遗憾的是,无情的现实否定了这种设想。事实上,不是所有的数据库都能可靠地实现“isNull”。某些数据库
除了读取该列并允许一次只能读一个给定列之外,并没有单独的措施来确定某一列是否为空。我们考虑的方法是:读取列值并“记住”它以备将来之用。但在需要数据转换时,该方法仍会出问题。</p>
<p><a name="25327"></a>在研究过多种解决方案后,我们勉强决定用 wasNull
方法代替 isNull 方法。wasNull 方法将只报告从给定的 ResultSet(或
CallableStatement)读取的最终值是否为 SQL NULL。</p>
<p><a name="25724"></a> </p>
<h3>A.10 Java 类型名/SQL 类型名的使用。</h3>
<p>在早些版本的规范中,我们用 getXXX 和 setXXX
方法检索结果、访问参数,其中的 XXX 代表 SQL 类型名。在修订版 JDBC
0.70 中,我们仍然使用 getXXX 和 setXXX 方法,但其中的 XXX 改为 Java
类型名。</p>
<p><a name="25736"></a>举例来说,getString 已取代 getChar,而 setShort
则已取代 setSmallInt。</p>
<p>新方法的语义与它们所取代的方法的语义本质上相同。但对 Java
程序员来说,Java 类型名的使用使每种方法的含义更清晰易懂。</p>
<p><a name="20150"></a> </p>
<p><a name="20120"></a> </p>
<p><br>
</p>
<hr>
<font size="-1"><a href="jdbc-spec.frame.html">
<p>目录</a> | <a href="jdbc-spec.frame14.html">上一页</a> | <a
href="jdbc-spec.frame16.html">下一页</a> </font></p>
<hr>
<address>
<a href="mailto:jdbc@wombat.eng.sun.com">jdbc@wombat.eng.sun.com</a> 或 <a
href="mailto:jdbc-odbc@wombat.eng.sun.com">jdbc-odbc@wombat.eng.sun.com</a>
</address>
<a href="../../../relnotes/SMICopyright.html"><font size="-1"><i>
<pre>版权所有© 1996 1997 Sun Microsystems, Inc. 保留所有权利。</i></font></a>
<!-- HTML generated by Suzette Pelouch on April 10, 1998 -->
</pre>
</body>
</html>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -