本概述是從《JDBCTM Database Access from JavaTM: A Tutorial and Annotated Reference》這本書中摘引來的。JavaSoft 目前正在準(zhǔn)備這本書。這本書是一本教程,同時也是 JDBC 的重要參考手冊,它將作為 Java 系列的組成部份,在 1997 年春季由 Addison-Wesley 出版公司出版。
8.1 概述
由于 SQL 數(shù)據(jù)類型和 Java 數(shù)據(jù)類型是不同的,因此需要某種機(jī)制在使用 Java類型的應(yīng)用程序和使用 SQL類型的數(shù)據(jù)庫之間來讀寫數(shù)據(jù)。
為此,JDBC 提供了 getXXX
和 setXXX
方法集、方法 registerOutParameter
和類 Types
。
本章匯集了影響各種類和接口的數(shù)據(jù)類型的有關(guān)信息,并列出所有的對應(yīng)關(guān)系表(這些表顯示了 SQL類型和 Java類型之間的映射關(guān)系)以便于參考。
8.2 將 SQL 數(shù)據(jù)類型映射為 Java類型
不幸的是,不同數(shù)據(jù)庫產(chǎn)品所支持的 SQL類型之間有很大的不同。即使不同的數(shù)據(jù)庫以相同的語義支持 SQL類型,它們也可能用不同的名稱。例如,絕大多數(shù)的主流數(shù)據(jù)庫都支持一種表示大型二進(jìn)制值的 SQL類型,但 Oracle 把這種類型叫做 LONG RAW
,Sybase 把它叫做 IMAGE
,Informix 卻把它叫做 BYTE
,而 DB2 又把它叫做 LONG VARCHAR FOR BIT DATA
。
幸運(yùn)的是,JDBC 程序員通常并不需要自己去關(guān)心目標(biāo)數(shù)據(jù)庫所用的實(shí)際 SQL類型的名稱。大多數(shù)時候,JDBC 程序員將根據(jù)一些現(xiàn)有的數(shù)據(jù)庫表來進(jìn)行編程。他們無須關(guān)心用于創(chuàng)建這些表的確切 SQL類型的名稱。
JDBC 在 java.sql.Types
類中定義了一系列的常規(guī) SQL類型標(biāo)識符。這些類型可用于表示那些最為常用的 SQL類型。在用 JDBC API 編程時,程序員通常可以使用這些 JDBC 類型來引用一般的 SQL類型,而無須關(guān)心目標(biāo)數(shù)據(jù)庫所用的確切 SQL類型的名稱。在下一節(jié)中將對這些 JDBC 類型進(jìn)行仔細(xì)說明。
程序員用到 SQL類型名稱的主要地方是在用 SQL 的 CREATE TABLE
語句創(chuàng)建新的數(shù)據(jù)庫表時。這種情況下,程序員必須注意應(yīng)該使用目標(biāo)數(shù)據(jù)庫所支持的 SQL類型名稱。如果需要知道各種 SQL類型在某個特定的數(shù)據(jù)庫中的行為的確切定義,我們建議查閱一下數(shù)據(jù)庫文檔。
如果想要編寫一種可在各種數(shù)據(jù)庫上創(chuàng)建表的可移植 JDBC 程序,用戶主要有兩個選擇。第一個選擇是:限制自己只使用那些被廣為接受的 SQL類型名稱(例如 INTEGER
、NUMERIC
或 VARCHAR
)。這些類型有可能能適應(yīng)所有的數(shù)據(jù)庫。第二個選擇是:用 java.sql.DatabaseMetaData.getTypeInfo
方法來找出給定的數(shù)據(jù)庫實(shí)際上支持哪些 SQL類型,然后選擇與給定 JDBC 類型相匹配的特定于數(shù)據(jù)庫的 SQL類型名。
JDBC 定義了一個從 JDBC 數(shù)據(jù)庫類型到 Java類型的標(biāo)準(zhǔn)映射。例如,JDBC 的 INTEGER
類型通常映射為 Java 的 int
類型。這可支持簡單的接口,將 JDBC 值讀寫為簡單的 Java類型。
Java
類型不必與 JDBC 類型完全形同;它們只須能夠用足夠的類型信息來代表 JDBC 類型,從而能正確地存儲和取出參數(shù)和從 SQL 語句恢復(fù)結(jié)果就可以了。例如,JavaString
對象可能并不能精確地與任何 JDBC CHAR
類型匹配,但它卻可給出足夠的類型信息來成功地表示 CHAR
、 VARCHAR
或 LONGVARCHAR
類型。
8.3 JDBC 類型
本節(jié)描述各種 JDBC 數(shù)據(jù)類型及其與標(biāo)準(zhǔn) SQL類型和 Java類型的關(guān)聯(lián)方式。
8.3.1 CHAR、 VARCHAR 和 LONGVARCHAR
JDBC 類型CHAR
、VARCHAR
和 LONGVARCHAR
密切相關(guān)。CHAR
表示固定長度的小字符串,VARCHAR
表示長度可變的小字符串,而 LONGVARCHAR
表示長度可變的大字符串。
與 JDBC CHAR
對應(yīng)的是 SQLCHAR
類型,其定義由 SQL-92 給出,且所有主要的數(shù)據(jù)庫都支持它。它接受用于指定字符串最大長度的參數(shù),例如 CHAR(12)
即定義了一個長度為 12 個字符的字符串。所有主要的數(shù)據(jù)庫都支持長度達(dá) 254 個字符的 CHAR
。
與 JDBC VARCHAR
對應(yīng)的是 SQLVARCHAR
類型,其定義由 SQL-92 給出,且所有的主要數(shù)據(jù)庫都支持它。它接受用于指定字符串最大長度的參數(shù),例如 VARCHAR(12)
即定義了一個最大長度為 12 個字符的字符串。所有主要數(shù)據(jù)庫都至少支持長度達(dá) 254 個字符的 VARCHAR
。當(dāng)把字符串的值賦給 VARCHAR
變量時,數(shù)據(jù)庫就記住該字符串的長度,使用 SELECT 時,它可以
返回準(zhǔn)確的原始字符串。
不幸的是,對于 JDBC LONGVARCHAR
類型,目前并沒有一致的 SQL 映射。所有主要數(shù)據(jù)庫都支持某種類型的長度可變的大字符串,這種字符串支持高達(dá)十億位字節(jié)的數(shù)據(jù),但 SQL類型名稱卻變化多樣。
Java 程序員不必區(qū)分 CHAR
、VARCHAR
和 LONGVARCHAR
這三種類型的 JDBC 字符串。它們都可表示為 JavaString
,并且在不知道所需要的確切數(shù)據(jù)類型時也可正確讀寫 SQL 語句。
CHAR
、VARCHAR
和 LONGVARCHAR
可映射為 String
或 char[]
,但 String
更適合于一般用法。同時, String
類能使 String
和 char[]
之間的轉(zhuǎn)換更為容易:它有一個用于將 String
對象轉(zhuǎn)換為 char[]
的方法,還有一個將 char[]
轉(zhuǎn)換為 String
對象的構(gòu)造函數(shù)。
必須提及的一個問題是:如何處理類型為 CHAR(n)
的固定長度的 SQL 字符串。答案是 JDBC 驅(qū)動程序(或 DBMS)將用適當(dāng)?shù)目崭駚磉M(jìn)行填補(bǔ)。因此,當(dāng)從數(shù)據(jù)庫中檢索 CHAR(n)
域時,驅(qū)動程序?qū)阉D(zhuǎn)換為長度為 n
的 JavaString
對象,對象末尾可能含有一些填補(bǔ)空格。反之,當(dāng)把 String
對象送到某個 CHAR(n)
域時,驅(qū)動程序和/或數(shù)據(jù)庫將在字符串的末尾填上一些必要的空格,使字符串的長度達(dá)到 n
。
方法 ResultSet.getString
用于分配和返回新的 String
對象。我們建議用它來從 CHAR
、VARCHAR
和LONGVARCHAR
域中檢索數(shù)據(jù)。它適用于檢索普通的數(shù)據(jù),但如果用 JDBC 類型LONGVARCHAR
來儲存多個兆字節(jié)的字符串時,用它進(jìn)行檢索將顯得十分笨拙。為此,ResultSet
接口中有兩個方法可供程序員將 LONGVARCHAR
值作為 Java 輸入流進(jìn)行檢索,之后可從該流中以任意大小的塊來讀取數(shù)據(jù)。這兩個方法是:getAsciiStream
和 getUnicodeStream
,它們將把儲存在 LONGVARCHAR
列的數(shù)據(jù)作為 Ascii 或 Unicode 字符流來傳送。
8.3.2 BINARY、VARBINARY 和 LONGVARBINARY
JDBC 類型BINARY
、VARBINARY
和 LONGVARBINARY
密切相關(guān)。BINARY
表示固定長度的小二進(jìn)制值, VARBINARY
表示長度可變化的小二進(jìn)制值,而 LONGVARBINARY
表示長度可變化的大二進(jìn)制值。
不幸的是,這些不同 BINARY
類型的使用還未被標(biāo)準(zhǔn)化,因而在各種主要數(shù)據(jù)庫提供的支持有很大的不同。
對應(yīng)于 JDBC BINARY
類型的 SQLBINARY
類型,是一種非標(biāo)準(zhǔn)的 SQL 擴(kuò)展,只在某些數(shù)據(jù)庫上才實(shí)現(xiàn)。它接受用于指定二進(jìn)制字節(jié)數(shù)的參數(shù)。例如,BINARY(12)
即定義了一個長度為 12 個字節(jié)的 binary 類型。通常,BINARY
值被限定在 254 個字節(jié)以內(nèi)。
對應(yīng)于 JDBC VARBINARY
類型的 SQLVARBINARY
類型,是一種非標(biāo)準(zhǔn)的 SQL 擴(kuò)展,只在某些數(shù)據(jù)庫上才實(shí)現(xiàn)。它接受用于指定二進(jìn)制字節(jié)最大數(shù)的參數(shù)。例如,VARBINARY(12)
即定義了一個長度最大可為 12 個字節(jié)的二進(jìn)制類型。通常,VARBINARY
的值被限定在 254 個字節(jié)以內(nèi)。當(dāng)把二進(jìn)制的值賦給 VARBINARY
變量時,數(shù)據(jù)庫就記住這個所賦值的長度,調(diào)用 SELECT
時,它返回準(zhǔn)確的原始值。
遺憾的是,目前還沒有一致的 SQL類型名稱與 JDBC LONGVARBINARY
類型相對應(yīng)。所有主要數(shù)據(jù)庫都支持某種類型的長度可變的大二進(jìn)制類型,它可支持高達(dá)十億個字節(jié)的數(shù)據(jù),但 SQL類型名稱卻變化多樣。
在 Java 中,BINARY
、VARBINARY
和 LONGVARBINARY
都可用同一 byte
數(shù)組來表示。由于可在不知道所需的 確切 BINARY
數(shù)據(jù)類型的情況下正確地讀寫 SQL 語句,因此,Java 程序員無需區(qū)分它們。
檢索 BINARY
和 VARBINARY
值時,我們建議使用 ResultSet.getBytes
。然而,如果類型為 JDBC LONGVARBINARY
的某列儲存的是幾兆字節(jié)長度的字節(jié)數(shù)組,則建議用方法 getBinaryStream
來檢索。與 LONGVARCHAR
的情形類似,該方法可以使 Java 程序員將 LONGVARBINARY
值作為 Java 輸入流檢索,然后可從該流中以更小的塊來讀取。
8.3.3 BIT
JDBC 類型BIT
代表一個位值,可為 0 或 1。
SQL-92 定義了 SQLBIT
類型。但與 JDBC BIT
類型不同,這種 SQL-92 BIT 類型帶參數(shù),用于定義固定長度的二進(jìn)制字符串。幸運(yùn)的是,SQL-92 也允許用簡單的非參數(shù)化的 BIT
類型來代表單個的二進(jìn)制數(shù)字。這種用法對應(yīng)于 JDBC BIT
類型。不幸的是,SQL-92 BIT
類型只有在 “完全” SQL-92 中才要求,且目前只有一部份主流數(shù)據(jù)庫支持它。因此,可移植的代碼也許寧愿用 JDBC SMALLINT
類型,這種類型已得到廣泛支持。
JDBC BIT
類型的 Java 映射的推薦類型是 Java 布爾型。
8.3.4 TINYINT
JDBC 類型TINYINT
代表一個 8 位無符號整數(shù),其值在 0 到 255 之間。
對應(yīng)的 SQL類型TINYINT
目前只有一部份的數(shù)據(jù)庫支持它。因此,可移植的代碼也許寧愿用 JDBC SMALLINT
類型,這種類型已得到廣泛支持。
JDBC TINYINT
類型的 Java 映射的推薦類型是 Javabyte
或 Javashort
。8 位的 Javabyte
類型代表一個有符號的整數(shù),其值在 -128 到 127 之間,因此對于大的 TINYINT
值它并非總合適,而 16 位的 Javashort
類型卻總能存儲所有的 TINYINT
值。
8.3.5 SMALLINT
JDBC 類型SMALLINT
代表一個 16 位的有符號整數(shù),其值在 -32768 和 32767 之間。
對應(yīng)的 SQL類型SMALLINT
,其定義由 SQL- 92 給出,并為所有主流數(shù)據(jù)庫所支持。SQL-92 標(biāo)準(zhǔn)將 SMALLINT
的精度留給實(shí)現(xiàn)去決定。但事實(shí)上,所有的主流數(shù)據(jù)庫都至少支持 16 位。
JDBC SMALLINT
類型的 Java 映射的推薦類型是 Javashort
類型。
8.3.6 INTEGER
JDBC 類型INTEGER
代表一個 32 位的有符號整數(shù),其值在 - 2147483648 和 2147483647 之間。
對應(yīng)的 SQL類型INTEGER
,其定義由 SQL- 92 給出,并為所有主流數(shù)據(jù)庫所廣為支持。SQL-92 標(biāo)準(zhǔn)將 INTEGER
的精度留給實(shí)現(xiàn)去決定。但事實(shí)上,所有的主流數(shù)據(jù)庫都至少支持 32 位。
INTEGER
類型
Java 映射的推薦類型是 Javaint
類型。
8.3.7 BIGINT
JDBC 類型BIGINT
代表一個 64 位的有符號整數(shù),其值在 -9223372036854775808 和 9223372036854775807 之間。
對應(yīng)的 SQL類型BIGINT
是 SQL 的一個非標(biāo)準(zhǔn)擴(kuò)展。事實(shí)上,目前還沒有任何數(shù)據(jù)庫實(shí)現(xiàn) SQLBIGINT
類型。我們建議在可移植的代碼中避免使用該類型。
BIGINT
類型的 Java 映射的推薦類型是 Java long 類型。
8.3.8 REAL
JDBC 類型REAL
代表一個有 7 位尾數(shù)的“單精度”浮點(diǎn)數(shù)。
對應(yīng)的 SQL類型REAL
,其定義由 SQL- 92 給出。雖然未得到普遍支持,但在主流數(shù)據(jù)庫中卻已得到廣泛支持。SQL-92 標(biāo)準(zhǔn)將 REAL
的精度留給實(shí)現(xiàn)去決定。但事實(shí)上,所有的支持 REAL
類型的主流數(shù)據(jù)庫都支持至少 7 位數(shù)的尾數(shù)精度。
REAL
類型的 Java 映射的推薦類型為 Javafloat
類型。
8.3.9 DOUBLE
JDBC 類型DOUBLE
代表一個有 15 位尾數(shù)的“雙精度”浮點(diǎn)數(shù)。
對應(yīng)的 SQL類型是 DOUBLE
PRECISION
,其定義由 SQL- 92 給出,并為主流數(shù)據(jù)庫所廣為支持。SQL-92 標(biāo)準(zhǔn)將 DOUBLE
PRECISION
的精度留給實(shí)現(xiàn)去決定。但事實(shí)上,所有支持 DOUBLE
PRECISION
類型的主流數(shù)據(jù)庫都支持至少 15 位數(shù)的尾數(shù)精度。
DOUBLE
類型的 Java 映射的推薦類型為 Javadouble
類型。
8.3.10 FLOAT
JDBC 類型FLOAT
基本上與 JDBC 類型DOUBLE
相同。我們同時提供了 FLOAT
和 DOUBLE
,其目的是與以前的 API 實(shí)現(xiàn)一致。但這卻有可能產(chǎn)生誤導(dǎo)。FLOAT
代表一個有 15 位尾數(shù)的“雙精度”浮點(diǎn)數(shù)。
對應(yīng)的 SQL類型FLOAT
,其定義由 SQL-92 給出。SQL-92 標(biāo)準(zhǔn)將 FLOAT
的精度留給實(shí)現(xiàn)去決定。但事實(shí)上,所有支持 FLOAT
類型的主流數(shù)據(jù)庫都支持至少 15 位數(shù)的尾數(shù)精度。
FLOAT
類型的 Java 映射的推薦類型為 Javadouble
類型。然而,由于 SQLFLOAT
和單精度的 Javafloat
類型間可能產(chǎn)生混淆,因此建議 JDBC 程序員通常選用 JDBC DOUBLE
類型而不選用 FLOAT
。
8.3.11 DECIMAL 和 NUMERIC
JDBC 類型DECIMAL
和 NUMERIC
兩者非常相似。它們都表示固定精度的十進(jìn)制值。
相應(yīng)的 SQL類型DECIMAL
和 NUMERIC
,其定義在 SQL-92 中給出,并得到廣泛支持。這些 SQL類型都帶有精度和比例參數(shù)。精度是所支持的十進(jìn)制數(shù)字的總位數(shù),比例是小數(shù)點(diǎn)后的數(shù)字位數(shù)。比例必須永遠(yuǎn)小于或等于精度。例如,值 "12.345" 有 5 位精度和 3 位比例,而值 ".11" 有 2 位精度和 2 位比例。JDBC 要求所有 DECIMAL
和 NUMERIC
類型都必須支持至少 15 位的精度和比例。
DECIMAL
和 NUMERIC
之間的唯一區(qū)別是 SQL-92 規(guī)范要求 NUMERIC
類型必須以確切指定的精度來表示,而對 DECIMAL
類型,它允許實(shí)現(xiàn)在創(chuàng)建該類型時所指定的精度以外再添加額外的精度。因此,創(chuàng)建為類型NUMERIC(12,4)
的列將總是用 12 位數(shù)來表示,而創(chuàng)建為類型DECIMAL(12,4)
的列則可用更大的位數(shù)來表示。
DECIMAL
和 NUMERIC
類型的 Java 映射的推薦類型是java.math.BigDecimal
,該 Java類型也用絕對精度來表示定點(diǎn)數(shù)。java.math.BigDecimal
類型提供了一些數(shù)學(xué)操作,可對 BigDecimal
類型與其它的 BigDecimal
類型、整數(shù)類型和浮點(diǎn)數(shù)類型進(jìn)行加、減、乘、除的運(yùn)算。
用于檢索 DECIMAL
和 NUMERIC
值的推薦方法是 ResultSet.getBigDecimal
。JDBC 還允許將這些 SQL類型作為簡單的 Strings
或 char
數(shù)組來訪問。因此,Java 程序員可用 getString
來檢索 DECIMAL
或 NUMERIC
結(jié)果。然而,這將使常見的用 DECIMAL
或 NUMERIC
來表示的貨幣值變得極為尷尬,因?yàn)樗馕吨鴳?yīng)用程序編程人員必須對字符串進(jìn)行數(shù)學(xué)運(yùn)算。當(dāng)然,也可將這些 SQL類型作為 Java 數(shù)值型類型來檢索。
8.3.12 DATE、TIME 和 TIMESTAMP
有三種 JDBC 類型與時間有關(guān):
- JDBC
DATE
類型表示一個由年、月、日組成的日期。對應(yīng)的是 SQLDATE
類型,其定義由 SQL-92 給出,但只有一部份主流數(shù)據(jù)庫實(shí)現(xiàn)它。某些數(shù)據(jù)庫提供了另外一些支持類似語義的 SQL類型。
- JDBC
TIME
類型表示一個由小時、分鐘和秒組成的時間。對應(yīng)的是 SQLTIME
類型,其定義由 SQL-92 給出,但只有一部份主流數(shù)據(jù)庫實(shí)現(xiàn)它。與 DATE
一樣,某些數(shù)據(jù)庫提供了另外一些支持類似語義的 SQL類型。
- JDBC
TIMESTAMP
類型表示 DATE
加上 TIME
,外加一個納秒域。對應(yīng)的 TIMESTAMP
類型,其定義由 SQL-92 給出,但只有少數(shù)幾個數(shù)據(jù)庫實(shí)現(xiàn)它。
由于標(biāo)準(zhǔn)的 Java 類 java.util.Date
并不與這三個 JDBC 日期—時間類型完全匹配(它含有 DATE
和 TIME
的信息但不含納秒信息),因此 JDBC 定義了三個 java.util.Date
的子類與 SQL類型對應(yīng)。它們是:
-
java.sql.Date
,對應(yīng)于 SQLDATE
信息。java.util.Date
基本類中的小時、分鐘和秒都設(shè)為 0。
-
java.sql.Time
,對應(yīng)于 SQLTIME
信息。java.util.Date
基本類中的年、月、日域設(shè)為 1970 年 1 月 1 日。這是 Java 紀(jì)元的“零”日期。
-
java.sql.Timestamp
,對應(yīng)于 SQLTIMESTAMP
信息。該類擴(kuò)展了 java.util.Date
,添加了納秒域。
所有這三個與時間有關(guān)的 JDBC 類都是 java.util.Date
的子類,因此它們可用在任何可以使用 java.util.Date
的地方。例如,國際化 (internationalization) 方法將 java.util.Date
對象用作變量,因此可將這三個與時間有關(guān)的 JDBC 類中任何一個的實(shí)例作為參數(shù)傳給國際化方法。
JDBC Timestamp
對象除了具有其父類的日期和時間成份外,還有一個獨(dú)立的納秒組件。如果將 java.sql.Timestamp
對象用于需要 java.util.Date
對象的地方,則納秒組件將丟失。但由于是以毫秒的精度來儲存 java.util.Date
對象的,因此將 java.sql.Timestamp
對象轉(zhuǎn)換為 java.util.Date
對象時可以保持這樣的精度。這可通過將納秒組件中的納秒轉(zhuǎn)換為毫秒(用納秒數(shù)除以 1,000,000)并將之添到 java.util.Date
對象中來實(shí)現(xiàn)。轉(zhuǎn)換中可能丟失高達(dá) 999,999 納秒,但所產(chǎn)生的 java.util.Date
對象將可精確到毫秒以內(nèi)。
下述代碼段將 java.sql.Timestamp
對象轉(zhuǎn)換為精度達(dá)到毫秒量級的 java.util.Date
對象:
Timestamp t = new Timestamp(100, 0, 1, 15, 45, 29, 987245732);
java.util.Date d;
d = new java.util.Date(t.getTime() + (t.getNanos() / 1000000));
8.4 映射示例
任何情況下,當(dāng) Java 程序要從數(shù)據(jù)庫中檢索數(shù)據(jù)時,必須存在某種形式的映射和數(shù)據(jù)轉(zhuǎn)換。大多數(shù)時候, JDBC 程序員將在知道其目標(biāo)數(shù)據(jù)庫機(jī)制的情況下進(jìn)行編程。例如,他們將知道數(shù)據(jù)庫含有哪些表、表中每一列的數(shù)據(jù)類型。因此,他們可使用 ResultSet
、 PreparedStatement
和 CallableStatement
接口中那些與類型有關(guān)的存取方法。本節(jié)給出三個示例,描述各種情形中所要求的數(shù)據(jù)映射和轉(zhuǎn)換。
8.4.1 簡單的 SQL 語句
在最常見的情形中,用戶將執(zhí)行簡單的 SQL 語句,然后取回含有結(jié)果的 ResultSet
對象。由數(shù)據(jù)庫返回并存放在 ResultSet
列的值,其類型為 JDBC 數(shù)據(jù)類型。調(diào)用 ResultSet.getXXX
方法將把該值檢索為 Java 數(shù)據(jù)類型。例如,如果某個 ResultSet
列含有一個 JDBC FLOAT
值,則方法 getDouble
將把它檢索為 Javadouble
類型。8.6.6 節(jié)所示的表顯示了哪些 getXXX
方法可檢索哪些 JDBC 類型(如果用戶不知道某個 ResultSet
列的類型,可通過調(diào)用 ResultSet.getMetaData
方法來獲得有關(guān)信息,然后再調(diào)用 ResultSetMetaData
的 getColumnType
或 getColumnTypeName
方法)。以下代碼段示范了如何獲得結(jié)果中各列的類型名稱:
String query = "select * from Table1";
ResultSet rs = stmt.executeQuery(query);
ResultSetMetaData rsmd = rs.getMetaData();
int columnCount = rsmd.getColumnCount();
for (int i = 1; i <= columnCount; i++) {
String s = rsmd.getColumnTypeName(i);
System.out.println ("Column " + i + " is type " + s);
}
在另一個可能的情況中,用戶將發(fā)送帶輸入?yún)?shù)的 SQL 語句。這種情況下,用戶通過調(diào)用 PreparedStatement.setXXX
方法為每個輸入?yún)?shù)賦值。例如, PreparedStatement.setLong(1, 2345678)
將把值 2345678
作為 Java的 long
類型賦給第一個參數(shù)。為了將 2345678
到數(shù)據(jù)庫中,驅(qū)動程序?qū)阉D(zhuǎn)換為 JDBC BIGINT
。驅(qū)動程序?qū)涯姆N JDBC 類型送到數(shù)據(jù)庫中是由 Java類型到 JDBC 類型的標(biāo)準(zhǔn)映射所決定的,如
還有一個情況是,用戶要調(diào)用已存儲過程,將值賦給其 INOUT 參數(shù),從結(jié)果中檢索值,然后從參數(shù)中檢索值。這種情形極為少見且相當(dāng)復(fù)雜,但它卻不失為映射和數(shù)據(jù)轉(zhuǎn)換的好范例。
這種情況下,首先要做的是用 PreparedStatement.setXXX
方法對 INOUT 參數(shù)賦值。此外,由于這些參數(shù)同時也用于輸出,因此程序員必須為每個參數(shù)注冊 JDBC 類型,該類型是數(shù)據(jù)庫所要返回給該參數(shù)的值的 JDBC 類型。這可用 CallableStatement.registerOutParameter
方法來完成,后者接受在類 Types
中所定義的 JDBC 類型作為其變量。程序員可以用 ResultSet.getXXX
方法系列來檢索返回給ResultSet
對象的結(jié)果,用 CallableStatement.getXXX
方法系列來檢索存放在輸出參數(shù)中的值。
用于 ResultSet
.getXXX
方法的 XXX
類型在某些情況下非常靈活。
用于 CallableStatement
.getXXX
方法的 XXX
類型必須映射為那個參數(shù)所注冊的 JDBC 類型。例如,如果數(shù)據(jù)庫應(yīng)返回類型為 JDBC
REAL
的輸出值,則該參數(shù)應(yīng)被注冊為 java.sql.Types.REAL
。因此,要檢索該 JDBC
REAL
值,必須調(diào)用 CallableStatement.getFloat
方法(從 JDBC 類型到 Java類型的映射在 8.6.1 節(jié)中的表中給出)。方法 getFloat
先把儲存在輸出參數(shù)中的值從 JDBC REAL
類型轉(zhuǎn)換為 Javafloat
類型,然后將它返回。為了適應(yīng)各種數(shù)據(jù)庫和使應(yīng)用程序具有更高的可移植性,建議先檢索 ResultSet
對象中的值,再檢索輸出參數(shù)中的值。
下述代碼示范的是調(diào)用名為 getTestData
的已存儲過程。它有兩個參數(shù),且都是 INOUT 參數(shù)。首先, Connection
對象 con
將創(chuàng)建 CallableStatement
對象 cstmt
。然后,方法 setByte
把第一個參數(shù)設(shè)置為 Javabyte
類型,其值為 25
。驅(qū)動程序?qū)?25
轉(zhuǎn)換為 JDBC TINYINT
類型并將之送到數(shù)據(jù)庫中。方法 setBigDecimal
用輸入值 83.75
來設(shè)置第二個參數(shù)。驅(qū)動程序?qū)堰@個 java.math.BigDecimal
對象轉(zhuǎn)換為 JDBC NUMERIC
值。接下來將這兩個參數(shù)注冊為 OUT 參數(shù),第一個參數(shù)注冊為 JDBC TINYINT
類型,第二個參數(shù)注冊為小數(shù)點(diǎn)后面帶兩位數(shù)字的 JDBC DECIMAL
類型。執(zhí)行 cstmt
后,就用 ResultSet.getXXX
方法將值從 ResultSet
對象中檢索出來。方法 getString
將第一列中的值作為 JavaString
對象獲取, getInt
將第二列中的值作為 Javaint
獲取,getInt
將第三列中的值作為 Javaint
獲取。
之后, CallableStatement.getXXX
方法檢索存放在輸出參數(shù)中的值。方法 getByte
將 JDBC
TINYINT
檢索為 Javabyte
,getBigDecimal
將 JDBC
DECIMAL
檢索為小數(shù)點(diǎn)后面帶有兩位數(shù)字的 java.math.BigDecimal
對象。注意,當(dāng)參數(shù)既是輸入?yún)?shù)同時又是輸出參數(shù)時,setXXX
方法所用的 Java類型與 getXXX
方法所用的相同(正如 setByte
和 getByte
中一樣)。registerOutParameter
方法將它注冊成由 Java類型映射來的 JDBC 類型(Javabyte
類型映射為 JDBC TINYINT
,如 8.6.2 節(jié)中的表所示)。
CallableStatement cstmt = con.prepareCall(
"{call getTestData(?, ?)}");
cstmt.setByte(1, 25);
cstmt.setBigDecimal(2, 83.75);
// 將第一個參數(shù)注冊為 JDBC TINYINT,第二個
// 參數(shù)注冊為小數(shù)點(diǎn)后面帶有兩位數(shù)字的 JDBC DECIMAL 類型
cstmt.registerOutParameter(1, java.sql.Types.TINYINT);
cstmt.registerOutParameter(2, java.sql.Types.DECIMAL, 2);
ResultSet rs = cstmt.executeUpdate();
// 檢索并打印結(jié)果中的值。
while(rs.next()) {
String name = rs.getString(1);
int score = rs.getInt(2);
int percentile = rs.getInt(3);
System.out.print("name = " + name + ", score = " + score + ", "
System.out.println("percentile = " + percentile);
// 檢索輸出參數(shù)中的值
byte x = cstmt.getByte(1);
java.math.BigDecimal n = cstmt.getBigDecimal(2, 2);
總之,CallableStatement.getXXX
和 PreparedStatement.setXXX
方法系列中的 XXX
是 Java類型。對于 setXXX
方法,驅(qū)動程序先把 Java類型轉(zhuǎn)換為 JDBC 類型,再把它送到數(shù)據(jù)庫中(使用 8.6.2 節(jié)中的表所示的標(biāo)準(zhǔn)映射)。對于 getXXX
方法, 驅(qū)動程序先把數(shù)據(jù)庫返回的 JDBC 類型轉(zhuǎn)換為 Java類型(用 8.6.1 節(jié)表中所示的標(biāo)準(zhǔn)映射),再把它返回給 getXXX
方法。
registerOutParameter
方法只接受 JDBC 類型的變量,而 setObject
方法卻可接受 JDBC 類型的變量。
注意,如果在可選的第三個變量的位置上提供了 JDBC 類型,則 setObject
方法將把參數(shù)值從 Java類型顯式地轉(zhuǎn)換為所指定的 JDBC 類型。如果沒有為 setObject
提供目標(biāo) JDBC 類型,則將把參數(shù)值轉(zhuǎn)換為 Java類型的標(biāo)準(zhǔn)映射 JDBC 類型(如 8.6.2 節(jié)的表中所示)。在將參數(shù)送到數(shù)據(jù)庫中之前,驅(qū)動程序都要進(jìn)行顯式或隱式轉(zhuǎn)換。
8.5 動態(tài)數(shù)據(jù)存取
大多數(shù)時候,用戶要存取的結(jié)果和參數(shù)其數(shù)據(jù)類型在編譯時是已知的。然而,有些應(yīng)用程序(例如普通的瀏覽器或查詢工具)在編譯時對它們所要存取的數(shù)據(jù)庫的機(jī)制并不知曉。因此,JDBC 除了支持靜態(tài)數(shù)據(jù)類型存取外,還支持類型完全動態(tài)確定的數(shù)據(jù)存取。
有三種方法和一個常量可用于訪問那些在編譯時其數(shù)據(jù)類型尚屬未知的值:
ResultSet.getObject
PreparedStatement.setObject
CallableStatement.getObject
java.sql.Types.OTHER
(用作 CallableStatement.registerOutParameter
的一個變量)
例如,如果應(yīng)用程序想要接受多種類型作為其 ResultSet
對象中的結(jié)果,它可以使用 ResultSet.getObject
方法。
ResultSet.getObject
和 CallableStatement.getObject
方法將值檢索為 JavaObject
。由于 Object
是所有 Java 對象的基本類,因此可將任何 Java 類的實(shí)例檢索為 Object
的實(shí)例。然而,以下 Java類型是內(nèi)置的“基本”類型,因此,它們不是類 Object
的實(shí)例: boolean
、char
、byte
、short
、int
、long
、 float
和 double
。因此,不能用 getObject
方法來檢索它們。然而,這些基本類型每種都有相應(yīng)的可用作 wrapper 的類。這些類的實(shí)例是對象,這意味著可用 ResultSet.getObject
和 CallableStatement.getObject
方法來檢索它們。第 67 頁中的表 8.6.3 顯示了從 JDBC 類型到 JavaObject
類型的映射。該表與 JDBC 類型到 Java類型的標(biāo)準(zhǔn)映射不同:在該表中,除了 JDBC TINYINT
和 JDBC SMALLINT
類型映射為 Java 類 Integer
之外,每一個基本的 Java類型都被替換為它們的 wrapper 類。
方法 getObject
還可用于檢索用戶定義的 Java類型。隨著抽象數(shù)據(jù)類型(ADT)和其它用戶定義的類型在某些數(shù)據(jù)庫系統(tǒng)中的出現(xiàn),一些提供者可能會發(fā)現(xiàn)用 getObject
來檢索這些類型將更方便。
8.6 數(shù)據(jù)類型映射表
本節(jié)含有以下表,它們是 JDBC 類型 和 Java 數(shù)據(jù)類型之間的映射關(guān)系表:??
8.6.1 節(jié) — 從 JDBC 類型映射到 Java類型??
8.6.2 節(jié) — 從 Java類型映射到 JDBC 類型??
8.6.3 節(jié) ─ 從 JDBC 類型映射到 JavaObject
類型??
8.6.4 節(jié) ─ 從 JavaObject
類型映射到 JDBC 類型??
8.6.5 節(jié) ─ 由 setObject
所進(jìn)行的轉(zhuǎn)換
8.6.6 節(jié) — 由 ResultSet.getXXX
方法所檢索的 JDBC 類型
8.6.1 從 JDBC 類型映射到 Java類型
