📄 jdbc2.0.frame9.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="100%">
<tr>
<td><font size="-1"><a href="jdbc2.0.frame.html">目录</a> | <a
href="jdbc2.0.frame8.html">上一页</a> | <a href="jdbc2.0.frame10.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="298134"></a> </p>
<h2>9 自定义 SQL 类型</h2>
<p>本章描述了 JDBC 2.0 API 所提供的对 SQL 结构化和 distinct 类型到 Java
类的映射方式进行自定义的支持。自定义机制最小程度地涉及了 JDBC
API 的扩展。新功能是现有 <code>getObject()</code> 和 <code>setObject()</code>
机制的扩展。</p>
<p><a name="297862"></a> </p>
<h3>9.1 类型映射方式</h3>
<p>使用 <code>java.util.Map</code> 的实例可以保留 SQL
自定义类型(结构化和 distinct 类型)和 Java
类之间的自定义映射方式。<code>java.util.Map </code>接口是 JDK 1.2
中新增的接口,它取代了 <code>java.util.Dictionary</code>
接口。这样的对象被称为<em>类型-映射</em>对象。类型-映射对象实现了从自定义类型的
SQL 名称转换成 <code>java.lang.Class</code> 类型的对象的功能。类型-映射对象确定了一种类,从中可以构造对象来包含给定的
SQL 自定义类型的数据。</p>
<p><a name="298573"></a>每个 JDBC <code>Connection </code>都有与之相关联的类型-映射对象。类型-映射对象所含的类型映射方式可以在该连接上的操作中转化
SQL
自定义类型的数据。此外,我们还提供了获取以及设置连接的类型映射的方法。例如,
</p>
<p><a name="298493"></a> </p>
<blockquote>
<pre>java.util.Map map = con.getTypeMap();
<code>con.setTypeMap(map);
</code>
</pre>
</blockquote>
<p><a name="298540"></a></p>
<code>
<p>Connection.getTypeMap()</code> 方法返回与连接相关联的类型-映射对象,而使用
<code>Connection.setTypeMap()</code> 则可设置新的类型映射方式。</p>
<p><a name="298604"></a>映射机制非常灵活。如果 JDBC
应用程序没有显式地初始化某连接的类型映射方式,则该连接上的操作就采用在第
<a href="jdbc2.0.frame8.html#297135">8</a>
章中所述的缺省映射方式。如果将自定义映射方式插入到 SQL 类型 <code>type-name</code>
的类型-映射中,则该连接上的所有操作对 <code>type-name</code>
类型的值都将使用该自定义映射方式。最后,我们要指出的是,当调用某个
<code>getXXX() </code>和 <code>setXXX()</code> 方法,甚至可以显式地提供类型-映射对象来覆盖与
<code>Connection</code> 相关联的自定义或缺省映射方式。</p>
<p><a name="297863"></a> </p>
<h3>9.2 Java 类约定</h3>
<p>出现在自定义的类型-映射中的 Java 类必须实现一种新接口 — <code>java.sql.SQLData</code>。<code>SQLData</code>
接口所含的方法可以将 SQL 自定义类型的实例转换成 Java
类实例,反之亦然。例如,<code>SQLData.readSQL()</code>
方法读入数据值流并且创建 Java 对象,而 <code>SQLData.writeSQL()</code>
将来自 Java
对象的值序列写入流中。我们希望通常由了解数据库架构的工具来生成这些方法。</p>
<p><a name="298799"></a><a name="298800"></a> </p>
<p><a name="291142"></a>这种 SQL 和 Java
之间基于流的数据交换方法在概念上类似于 Java
对象序列化。数据将从 JDBC 驱动程序所提供的 SQL
数据流中读出或者写入。可以基于多种网络协议和数据格式来实现
SQL 数据流。可以基于任何逻辑数据表示来实现 SQL
数据流,在这种逻辑数据表示中,在对结构化类型进行“深度优先”遍历时可以从数据流中读出(或写入数据流)叶子
SQL 数据项(SQL 结构化类型的组成元素)。这就是说,SQL
结构化类型的属性在流中的出现顺序实际上就是属性在类型中的声明顺序,而且在下一个属性出现之前,每个(也许是结构化的)属性值在流中全部(其结构被递归地详细说明了)出现。对于采用了继承的
SQL
结构化类型的数据,属性在流中的出现顺序必须是其继承顺序。这就是说,父类型的属性必须出现在子类型的属性之前。如果采用了多重继承,则父类型的属性在流中的出现顺序应该是父类型在类型声明中的排列顺序。本协议不需要数据库服务器了解
Java。</p>
<p><a name="293642"></a> </p>
<h3>9.3 SQL 数据流</h3>
<p>本节描述了流接口,即 SQLInput 和 SQLOutput。它们支持对 SQL 到 Java
的类型映射方式进行自定义。</p>
<p><a name="292203"></a> </p>
<h4>9.3.1 检索数据</h4>
<p>当从数据库检索到 SQL 结构化和 distinct
类型的数据时,数据就“到达”了实现 <code>SQLInput</code> 接口的流。<code>SQLInput</code>
接口所含方法可以从流中顺序地读入各个数据值。下例说明了如何使用
<code>SQLInput</code> 流来为 <code>SQLData</code> 对象的域提供数值。<code>SQLData</code>
对象(例中的 <code>this</code> 对象)包含了三个持久的域:<code>String s</code>、<code>Blob
blob</code> 和 <code>Employee emp</code>。</p>
<p><a name="298685"></a> </p>
<blockquote>
<pre>this.str = sqlin.readString();
this.blob = sqlin.readBlob();
this.emp = (Employee)sqlin.readObject();
</pre>
</blockquote>
<p><a name="289633"></a></p>
<code>
<p>SQLInput.readString()</code> 方法从流中读取 <code>String</code>
值。可以使用 <code>SQLInput.readBlob()</code> 方法来从流中检索 <code>Blob</code>
值。缺省情况下使用 SQL 定位符来实现 <code>Blob</code> 接口,因此调用
<code>readBlob() </code>不会在 JDBC 客户机上具体实现 blob
的内容。可以使用 <code>SQLInput.readObject() </code>方法来从流中返回对象引用。在示例中,所返回的
<code>Object</code> 被限定为 <code>Employee</code>。</p>
<p><a name="298692"></a><code>SQLInput</code> 接口上定义了很多附加的 <code>readXXX()</code>
方法,可以读取每种 JDBC 类型的数据。可以调用 <code>SQLInput.wasNull()</code>
方法来检查 <code>readXXX()</code> 方法的返回值是否为空。</p>
<p><a name="289634"></a> </p>
<h4>9.3.2 存储数据</h4>
<p>当经由 <code>setXXX()</code> 方法将 <code>SQLData</code>
对象作为输入参数传递给 JDBC 时,JDBC 驱动程序就调用该对象的 <code>SQLData.writeSql()</code>
方法来获得对象内容的流表示。<code>writeSQL() </code>方法将来自对象的数据作为
SQL 自定义类型的表示写入 <code>SQLOutput</code>
流中。通常由一些工具依据 SQL 类型定义来生成 <code>writeSQL() </code>方法。下例说明了
<code>SQLOutput</code> 流对象的使用方法。</p>
<p><a name="289851"></a> </p>
<blockquote>
<pre>sqlout.writeString(this.str);
sqlout.writeBlob(this.blob);
sqlout.writeObject(this.emp);
</pre>
</blockquote>
<p><a name="298732"></a></p>
<p>下例说明了如何将 <code>SQLData</code> 对象的内容写入 <code>SQLOutput</code>
流中。<code>SQLData</code> 对象(即例中的 <code>this</code>
对象)包含了三个持久的域:即 <code>String s</code>、<code>Blob blob</code>
和 <code>Employee emp</code>。每个域将被依次写入 <code>SQLOutput</code> 流 <code>sqlout</code>
中。<code>SQLOutput</code> 接口所含的附加方法用于写入每种 JDBC 类型。</p>
<p><a name="298943"></a> </p>
<h3>9.4 示例</h3>
<h4>9.4.1 SQL 结构化类型的示例</h4>
<p>以下 SQL 示例定义了结构化类型 PERSON、FULLNAME 和 RESIDENCE。本例还定义了拥有
PERSON 和 RESIDENCE
类型的行的几个表,并且在每个表中都插入了一行,以便使行之间相互引用。最后,本例对表进行了查询。</p>
<p><a name="298946"></a> </p>
<blockquote>
<pre>CREATE TYPE RESIDENCE
(
DOOR NUMERIC(6),
STREET VARCHAR(100),
CITY VARCHAR(50),
OCCUPANT REF(PERSON)
);
CREATE TYPE FULLNAME
(
FIRST VARCHAR(50),
LAST VARCHAR(50)
);
CREATE TYPE PERSON
(
NAME FULLNAME,
HEIGHT NUMERIC,
WEIGHT NUMERIC,
HOME REF(RESIDENCE)
);
CREATE TABLE HOMES OF RESIDENCE (OID REF(RESIDENCE)
VALUES ARE SYSTEM GENERATED);
CREATE TABLE PEOPLE OF PERSON (OID REF(PERSON)
VALUES ARE SYSTEM GENERATED);
INSERT INTO PEOPLE (SURNAME, HEIGHT, WEIGHT) VALUES
(
FULLNAME('DAFFY', 'DUCK'),
4,
58
);
INSERT INTO HOMES (DOOR, STREET, CITY, OCCUPANT) VALUES
(
1234,
'CARTOON LANE',
'LOS ANGELES',
(SELECT OID FROM PEOPLE P WHERE P.NAME.FIRST = 'DAFFY')
);
UPDATE PEOPLE SET HOME = (SELECT OID FROM HOMES H WHERE
H.OCCUPANT->NAME.FIRST = 'DAFFY') WHERE
FULLNAME.FIRST = 'DAFFY'
</pre>
</blockquote>
<p><a name="298992"></a></p>
<p><a name="298993"></a>上例构造了三个结构化类型实例,类型 PERSON、
FULLNAME 和 RESIDENCE 分别对应其中一个。FULLNAME 属性内嵌在 PERSON 中。PERSON
和 RESIDENCE 实例作为表中的行来存储,并且经由 Ref 属性相互引用。</p>
<p><a name="298994"></a>以下的 Java 类代表的是以上的 SQL
结构化类型。我们希望通常用 SQL 到 Java
的映射工具来生成这些类,映射工具从目录表中读取结构化类型的定义,而且根据工具使用者对基本域的名称映射方式和类型映射方式的自定义生成如下所示的
Java 类。</p>
<p><a name="301075"></a>注意:JDBC 2.0 没有提供访问 SQL 到 Java
映射工具所需的元数据的标准 API。提供这种类型的元数据将引入许多对
SQL3 类型模型错综复杂的依赖性,因此目前没有考虑这样做。</p>
<p><a name="301071"></a>在下列的每一种类中,<code>SQLData.readSQL()</code>
方法读取属性的顺序就是属性在数据库中的相应结构化类型的定义中的出现顺序(即“行序,深度优先”顺序:读取下一个属性之前,递归地读取每个属性的完整结构)。同样,<code>SQLData.writeSQL()</code>
将数据写入流也是依据这种顺序。</p>
<p><a name="301069"></a> </p>
<blockquote>
<pre>public class Residence implements SQLData {
public int door;
public String street;
public String city;
public Ref occupant;
private String sql_type;
public String getSQLTypeName() { return sql_type; }
public void readSQL (SQLInput stream, String type)
throws SQLException {
sql_type = type;
door = stream.readInt();
street = stream.readString();
city = stream.readString();
occupant = stream.readRef();
}
public void writeSQL (SQLOutput stream) throws SQLException {
stream.writeInt(door);
stream.writeString(street);
stream.writeString(city);
stream.writeRef(occupant);
}
}
public class Fullname implements SQLData {
public String first;
public String last;
private String sql_type;
public String getSQLTypeName() { return sql_type; }
public void readSQL (SQLInput stream, String type)
throws SQLException {
sql_type = type;
first = stream.readString();
last = stream.readString();
}
public void writeSQL (SQLOutput stream) throws SQLException {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -