JDBC資料型別、Java資料型別、標準sql型別
本概述是從《JDBCTM Database Access from JavaTM: A Tutorial and Annotated Reference》這本書中摘引來的。JavaSoft 目前正在準備這本書。這本書是一本教程,同時也是 JDBC 的重要參考手冊,它將作為 Java 系列的組成部份,在 1997 年春季由 Addison-Wesley 出版公司出版。
8.1 概述
由於 SQL 資料型別和 Java 資料型別是不同的,因此需要某種機制在使用 Java 型別的應用程式和使用 SQL 型別的資料庫之間來讀寫資料。
為此,JDBC 提供了 getXXX
和 setXXX
registerOutParameter
和類 Types
。
本章彙集了影響各種類和介面的資料型別的有關資訊,並列出所有的對應關係表(這些表顯示了 SQL 型別和 Java 型別之間的對映關係)以便於參考。
8.2 將 SQL 資料型別對映為 Java 型別
不幸的是,不同資料庫產品所支援的 SQL 型別之間有很大的不同。即使不同的資料庫以相同的語義支援 SQL 型別,它們也可能用不同的名稱。例如,絕大多數的主流資料庫都支援一種表示大型二進位制值的 SQL 型別,但 Oracle 把這種型別叫做 LONG RAW
,Sybase 把它叫做 IMAGE
BYTE
,而 DB2 又把它叫做 LONG VARCHAR FOR BIT DATA
。
幸運的是,JDBC 程式設計師通常並不需要自己去關心目標資料庫所用的實際 SQL 型別的名稱。大多數時候,JDBC 程式設計師將根據一些現有的資料庫表來進行程式設計。他們無須關心用於建立這些表的確切 SQL 型別的名稱。
JDBC 在 java.sql.Types
類中定義了一系列的常規 SQL 型別識別符號。這些型別可用於表示那些最為常用的 SQL 型別。在用 JDBC API 程式設計時,程式設計師通常可以使用這些 JDBC 型別來引用一般的 SQL 型別,而無須關心目標資料庫所用的確切 SQL 型別的名稱。在下一節中將對這些 JDBC 型別進行仔細說明。
程式設計師用到 SQL 型別名稱的主要地方是在用 SQL 的 CREATE TABLE
語句建立新的資料庫表時。這種情況下,程式設計師必須注意應該使用目標資料庫所支援的 SQL 型別名稱。如果需要知道各種 SQL 型別在某個特定的資料庫中的行為的確切定義,我們建議查閱一下資料庫文件。
如果想要編寫一種可在各種資料庫上建立表的可移植 JDBC 程式,使用者主要有兩個選擇。第一個選擇是:限制自己只使用那些被廣為接受的 SQL 型別名稱(例如 INTEGER
、NUMERIC
或VARCHAR
)。這些型別有可能能適應所有的資料庫。第二個選擇是:用 java.sql.DatabaseMetaData.getTypeInfo
方法來找出給定的資料庫實際上支援哪些 SQL 型別,然後選擇與給定 JDBC 型別相匹配的特定於資料庫的 SQL 型別名。
JDBC 定義了一個從 JDBC 資料庫型別到 Java 型別的標準對映。例如,JDBC 的 INTEGER
型別通常對映為 Java 的 int
型別。這可支援簡單的介面,將 JDBC 值讀寫為簡單的 Java 型別。
Java 型別不必與 JDBC 型別完全形同;它們只須能夠用足夠的型別資訊來代表 JDBC 型別,從而能正確地儲存和取出引數和從 SQL 語句恢復結果就可以了。例如,Java String
物件可能並不能精確地與任何 JDBC CHAR
型別匹配,但它卻可給出足夠的型別資訊來成功地表示 CHAR
、 VARCHAR
或 LONGVARCHAR
型別。
8.3 JDBC 型別
本節描述各種 JDBC 資料型別及其與標準 SQL 型別和 Java 型別的關聯方式。
8.3.1 CHAR、 VARCHAR 和 LONGVARCHAR
JDBC 型別 CHAR
、VARCHAR
和 LONGVARCHAR
密切相關。CHAR
表示固定長度的小字串,VARCHAR
表示長度可變的小字串,而 LONGVARCHAR
表示長度可變的大字串。
與 JDBC CHAR
對應的是 SQL CHAR
型別,其定義由 SQL-92 給出,且所有主要的資料庫都支援它。它接受用於指定字串最大長度的引數,例如 CHAR(12)
即定義了一個長度為 12 個字元的字串。所有主要的資料庫都支援長度達 254 個字元的 CHAR
。
與 JDBC VARCHAR
對應的是 SQL VARCHAR
型別,其定義由 SQL-92 給出,且所有的主要資料庫都支援它。它接受用於指定字串最大長度的引數,例如 VARCHAR(12)
即定義了一個最大長度為 12 個字元的字串。所有主要資料庫都至少支援長度達 254 個字元的 VARCHAR
。當把字串的值賦給 VARCHAR
變數時,資料庫就記住該字串的長度,使用 SELECT 時,它可以
返回準確的原始字串。
不幸的是,對於 JDBC LONGVARCHAR
型別,目前並沒有一致的 SQL 對映。所有主要資料庫都支援某種型別的長度可變的大字串,這種字串支援高達十億位位元組的資料,但 SQL 型別名稱卻變化多樣。
Java 程式設計師不必區分 CHAR
、VARCHAR
和 LONGVARCHAR
這三種類型的 JDBC 字串。它們都可表示為 Java String
,並且在不知道所需要的確切資料型別時也可正確讀寫 SQL 語句。
CHAR
、VARCHAR
和 LONGVARCHAR
可對映為 String
或 char[]
,但 String
更適合於一般用法。同時, String
類能使 String
和 char[]
之間的轉換更為容易:它有一個用於將 String
物件轉換為 char[]
的方法,還有一個將 char[]
轉換為 String
物件的建構函式。
必須提及的一個問題是:如何處理型別為 CHAR(n)
的固定長度的 SQL 字串。答案是 JDBC 驅動程式(或 DBMS)將用適當的空格來進行填補。因此,當從資料庫中檢索 CHAR(n)
域時,驅動程式將把它轉換為長度為 n
的 Java String
物件,物件末尾可能含有一些填補空格。反之,當把 String
物件送到某個 CHAR(n)
域時,驅動程式和/或資料庫將在字串的末尾填上一些必要的空格,使字串的長度達到 n
。
方法 ResultSet.getString
用於分配和返回新的 String
物件。我們建議用它來從 CHAR
、VARCHAR
和LONGVARCHAR
域中檢索資料。它適用於檢索普通的資料,但如果用 JDBC 型別LONGVARCHAR
來儲存多個兆位元組的字串時,用它進行檢索將顯得十分笨拙。為此,ResultSet
介面中有兩個方法可供程式設計師將 LONGVARCHAR
值作為 Java 輸入流進行檢索,之後可從該流中以任意大小的塊來讀取資料。這兩個方法是:getAsciiStream
和 getUnicodeStream
,它們將把儲存在 LONGVARCHAR
列的資料作為 Ascii 或 Unicode 字元流來傳送。
8.3.2 BINARY、VARBINARY 和 LONGVARBINARY
JDBC 型別 BINARY
、VARBINARY
和 LONGVARBINARY
密切相關。BINARY
表示固定長度的小二進位制值, VARBINARY
表示長度可變化的小二進位制值,而 LONGVARBINARY
表示長度可變化的大二進位制值。
不幸的是,這些不同 BINARY
型別的使用還未被標準化,因而在各種主要資料庫提供的支援有很大的不同。
對應於 JDBC BINARY
型別的 SQL BINARY
型別,是一種非標準的 SQL 擴充套件,只在某些資料庫上才實現。它接受用於指定二進位制位元組數的引數。例如,BINARY(12)
即定義了一個長度為 12 個位元組的 binary 型別。通常,BINARY
值被限定在 254 個位元組以內。
對應於 JDBC VARBINARY
型別的 SQL VARBINARY
型別,是一種非標準的 SQL 擴充套件,只在某些資料庫上才實現。它接受用於指定二進位制位元組最大數的引數。例如,VARBINARY(12)
即定義了一個長度最大可為 12 個位元組的二進位制型別。通常,VARBINARY
的值被限定在 254 個位元組以內。當把二進位制的值賦給 VARBINARY
變數時,資料庫就記住這個所賦值的長度,呼叫 SELECT
時,它返回準確的原始值。
遺憾的是,目前還沒有一致的 SQL 型別名稱與 JDBC LONGVARBINARY
型別相對應。所有主要資料庫都支援某種型別的長度可變的大二進位制型別,它可支援高達十億個位元組的資料,但 SQL 型別名稱卻變化多樣。
在 Java 中,BINARY
、VARBINARY
和 LONGVARBINARY
都可用同一 byte
陣列來表示。由於可在不知道所需的確切 BINARY
資料型別的情況下正確地讀寫 SQL 語句,因此,Java 程式設計師無需區分它們。
檢索 BINARY
和 VARBINARY
值時,我們建議使用 ResultSet.getBytes
。然而,如果型別為 JDBC LONGVARBINARY
的某列儲存的是幾兆位元組長度的位元組陣列,則建議用方法getBinaryStream
來檢索。與 LONGVARCHAR
的情形類似,該方法可以使 Java 程式設計師將 LONGVARBINARY
值作為 Java 輸入流檢索,然後可從該流中以更小的塊來讀取。
8.3.3 BIT
JDBC 型別 BIT
代表一個位值,可為 0 或 1。SQL-92 定義了 SQL BIT
型別。但與 JDBC BIT
型別不同,這種 SQL-92 BIT 型別帶引數,用於定義固定長度的二進位制字串。幸運的是,SQL-92 也允許用簡單的非引數化的 BIT
型別來代表單個的二進位制數字。這種用法對應於 JDBC BIT
型別。不幸的是,SQL-92 BIT
型別只有在 “完全” SQL-92 中才要求,且目前只有一部份主流資料庫支援它。因此,可移植的程式碼也許寧願用 JDBC SMALLINT
型別,這種型別已得到廣泛支援。
JDBC BIT
型別的 Java 對映的推薦型別是 Java 布林型。
8.3.4 TINYINT
JDBC 型別 TINYINT
代表一個 8 位無符號整數,其值在 0 到 255 之間。
對應的 SQL 型別 TINYINT
目前只有一部份的資料庫支援它。因此,可移植的程式碼也許寧願用 JDBC SMALLINT
型別,這種型別已得到廣泛支援。
JDBC TINYINT
型別的 Java 對映的推薦型別是 Java byte
或 Java short
。8 位的 Java byte
型別代表一個有符號的整數,其值在 -128 到 127 之間,因此對於大的 TINYINT
值它並非總合適,而 16 位的 Java short
型別卻總能儲存所有的 TINYINT
值。
8.3.5 SMALLINT
JDBC 型別 SMALLINT
代表一個 16 位的有符號整數,其值在 -32768 和 32767 之間。
對應的 SQL 型別 SMALLINT
,其定義由 SQL- 92 給出,併為所有主流資料庫所支援。SQL-92 標準將 SMALLINT
的精度留給實現去決定。但事實上,所有的主流資料庫都至少支援 16 位。
JDBC SMALLINT
型別的 Java 對映的推薦型別是 Java short
型別。
8.3.6 INTEGER
JDBC 型別 INTEGER
代表一個 32 位的有符號整數,其值在 - 2147483648 和 2147483647 之間。
對應的 SQL 型別 INTEGER
,其定義由 SQL- 92 給出,併為所有主流資料庫所廣為支援。SQL-92 標準將 INTEGER
的精度留給實現去決定。但事實上,所有的主流資料庫都至少支援 32 位。
INTEGER
型別 Java 對映的推薦型別是 Java int
型別。
8.3.7 BIGINT
JDBC 型別 BIGINT
代表一個 64 位的有符號整數,其值在 -9223372036854775808 和 9223372036854775807 之間。
對應的 SQL 型別 BIGINT
是 SQL 的一個非標準擴充套件。事實上,目前還沒有任何資料庫實現 SQL BIGINT
型別。我們建議在可移植的程式碼中避免使用該型別。
BIGINT
型別的 Java 對映的推薦型別是 Java long 型別。
8.3.8 REAL
JDBC 型別 REAL
代表一個有 7 位尾數的“單精度”浮點數。
對應的 SQL 型別 REAL
,其定義由 SQL- 92 給出。雖然未得到普遍支援,但在主流資料庫中卻已得到廣泛支援。SQL-92 標準將 REAL
的精度留給實現去決定。但事實上,所有的支援 REAL
型別的主流資料庫都支援至少 7 位數的尾數精度。
REAL
型別的 Java 對映的推薦型別為 Java float
型別。
8.3.9 DOUBLE
JDBC 型別 DOUBLE
代表一個有 15 位尾數的“雙精度”浮點數。
對應的 SQL 型別是 DOUBLE
PRECISION
,其定義由 SQL- 92 給出,併為主流資料庫所廣為支援。SQL-92 標準將 DOUBLE
PRECISION
的精度留給實現去決定。但事實上,所有支援 DOUBLE
PRECISION
型別的主流資料庫都支援至少 15 位數的尾數精度。
DOUBLE
型別的 Java 對映的推薦型別為 Java double
型別。
8.3.10 FLOAT
JDBC 型別 FLOAT
基本上與 JDBC 型別 DOUBLE
相同。我們同時提供了 FLOAT
和 DOUBLE
,其目的是與以前的 API 實現一致。但這卻有可能產生誤導。FLOAT
代表一個有 15 位尾數的“雙精度”浮點數。
對應的 SQL 型別 FLOAT
,其定義由 SQL-92 給出。SQL-92 標準將 FLOAT
的精度留給實現去決定。但事實上,所有支援 FLOAT
型別的主流資料庫都支援至少 15 位數的尾數精度。
FLOAT
型別的 Java 對映的推薦型別為 Java double
型別。然而,由於 SQL FLOAT
和單精度的 Java float
型別間可能產生混淆,因此建議 JDBC 程式設計師通常選用 JDBC DOUBLE
型別而不選用 FLOAT
。
8.3.11 DECIMAL 和 NUMERIC
JDBC 型別 DECIMAL
和 NUMERIC
兩者非常相似。它們都表示固定精度的十進位制值。
相應的 SQL 型別 DECIMAL
和 NUMERIC
,其定義在 SQL-92 中給出,並得到廣泛支援。這些 SQL 型別都帶有精度和比例引數。精度是所支援的十進位制數字的總位數,比例是小數點後的數字位數。比例必須永遠小於或等於精度。例如,值 "12.345" 有 5 位精度和 3 位比例,而值 ".11" 有 2 位精度和 2 位比例。JDBC 要求所有 DECIMAL
和 NUMERIC
型別都必須支援至少 15 位的精度和比例。
DECIMAL
和 NUMERIC
之間的唯一區別是 SQL-92 規範要求 NUMERIC
型別必須以確切指定的精度來表示,而對 DECIMAL
型別,它允許實現在建立該型別時所指定的精度以外再新增額外的精度。因此,建立為型別 NUMERIC(12,4)
的列將總是用 12 位數來表示,而建立為型別 DECIMAL(12,4)
的列則可用更大的位數來表示。
DECIMAL
和 NUMERIC
型別的 Java 對映的推薦型別是 java.math.BigDecimal
,該 Java 型別也用絕對精度來表示定點數。java.math.BigDecimal
型別提供了一些數學操作,可對BigDecimal
型別與其它的 BigDecimal
型別、整數型別和浮點數型別進行加、減、乘、除的運算。
用於檢索 DECIMAL
和 NUMERIC
值的推薦方法是 ResultSet.getBigDecimal
。JDBC 還允許將這些 SQL 型別作為簡單的 Strings
或 char
陣列來訪問。因此,Java 程式設計師可用getString
來檢索 DECIMAL
或 NUMERIC
結果。然而,這將使常見的用 DECIMAL
或 NUMERIC
來表示的貨幣值變得極為尷尬,因為它意味著應用程式程式設計人員必須對字串進行數學運算。當然,也可將這些 SQL 型別作為 Java 數值型型別來檢索。
8.3.12 DATE、TIME 和 TIMESTAMP
有三種 JDBC 型別與時間有關:
- JDBC
DATE
型別表示一個由年、月、日組成的日期。對應的是 SQLDATE
型別,其定義由 SQL-92 給出,但只有一部份主流資料庫實現它。某些資料庫提供了另外一些支援類似語義的 SQL 型別。 - JDBC
TIME
型別表示一個由小時、分鐘和秒組成的時間。對應的是 SQLTIME
型別,其定義由 SQL-92 給出,但只有一部份主流資料庫實現它。與DATE
一樣,某些資料庫提供了另外一些支援類似語義的 SQL 型別。 - JDBC
TIMESTAMP
型別表示DATE
加上TIME
,外加一個納秒域。對應的TIMESTAMP
型別,其定義由 SQL-92 給出,但只有少數幾個資料庫實現它。
由於標準的 Java 類 java.util.Date
並不與這三個 JDBC 日期—時間型別完全匹配(它含有 DATE
和 TIME
的資訊但不含納秒資訊),因此 JDBC 定義了三個 java.util.Date
的子類與 SQL 型別對應。它們是:
java.sql.Date
,對應於 SQLDATE
資訊。java.util.Date
基本類中的小時、分鐘和秒都設為 0。java.sql.Time
,對應於 SQLTIME
資訊。java.util.Date
基本類中的年、月、日域設為 1970 年 1 月 1 日。這是 Java 紀元的“零”日期。java.sql.Timestamp
,對應於 SQLTIMESTAMP
資訊。該類擴充套件了java.util.Date
,添加了納秒域。
所有這三個與時間有關的 JDBC 類都是 java.util.Date
的子類,因此它們可用在任何可以使用 java.util.Date
的地方。例如,國際化 (internationalization) 方法將 java.util.Date
物件用作變數,因此可將這三個與時間有關的 JDBC 類中任何一個的例項作為引數傳給國際化方法。
JDBC Timestamp
物件除了具有其父類的日期和時間成份外,還有一個獨立的納秒元件。如果將 java.sql.Timestamp
物件用於需要 java.util.Date
物件的地方,則納秒元件將丟失。但由於是以毫秒的精度來儲存 java.util.Date
物件的,因此將 java.sql.Timestamp
物件轉換為 java.util.Date
物件時可以保持這樣的精度。這可通過將納秒元件中的納秒轉換為毫秒(用納秒數除以 1,000,000)並將之添到 java.util.Date
物件中來實現。轉換中可能丟失高達 999,999 納秒,但所產生的 java.util.Date
物件將可精確到毫秒以內。
下述程式碼段將 java.sql.Timestamp
物件轉換為精度達到毫秒量級的 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 對映示例
任何情況下,當 Java 程式要從資料庫中檢索資料時,必須存在某種形式的對映和資料轉換。大多數時候, JDBC 程式設計師將在知道其目標資料庫機制的情況下進行程式設計。例如,他們將知道資料庫含有哪些表、表中每一列的資料型別。因此,他們可使用 ResultSet
、 PreparedStatement
和 CallableStatement
介面中那些與型別有關的存取方法。本節給出三個示例,描述各種情形中所要求的資料對映和轉換。
8.4.1 簡單的 SQL 語句
在最常見的情形中,使用者將執行簡單的 SQL 語句,然後取回含有結果的 ResultSet
物件。由資料庫返回並存放在 ResultSet
列的值,其型別為 JDBC 資料型別。呼叫ResultSet.getXXX
方法將把該值檢索為 Java 資料型別。例如,如果某個 ResultSet
列含有一個 JDBC FLOAT
值,則方法 getDouble
將把它檢索為 Java double
型別。8.6.6 節所示的表顯示了哪些 getXXX
方法可檢索哪些 JDBC 型別(如果使用者不知道某個 ResultSet
列的型別,可通過呼叫 ResultSet.getMetaData
方法來獲得有關資訊,然後再呼叫ResultSetMetaData
的 getColumnType
或 getColumnTypeName
方法)。以下程式碼段示範瞭如何獲得結果中各列的型別名稱:
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); }
8.4.2 帶 IN 引數的 SQL 語句
在另一個可能的情況中,使用者將傳送帶輸入引數的 SQL 語句。這種情況下,使用者通過呼叫 PreparedStatement.setXXX
方法為每個輸入引數賦值。例如,PreparedStatement.setLong(1, 2345678)
將把值 2345678
作為 Java 的 long
型別賦給第一個引數。為了將 2345678
到資料庫中,驅動程式將把它轉換為 JDBC BIGINT
。驅動程式將把哪種 JDBC 型別送到資料庫中是由 Java 型別到 JDBC 型別的標準對映所決定的
8.4.3 帶 OUT 引數的 SQL 語句
還有一個情況是,使用者要呼叫已儲存過程,將值賦給其 INOUT 引數,從結果中檢索值,然後從引數中檢索值。這種情形極為少見且相當複雜,但它卻不失為對映和資料轉換的好範例。
這種情況下,首先要做的是用 PreparedStatement.setXXX
方法對 INOUT 引數賦值。此外,由於這些引數同時也用於輸出,因此程式設計師必須為每個引數註冊 JDBC 型別,該型別是資料庫所要返回給該引數的值的 JDBC 型別。這可用 CallableStatement.registerOutParameter
方法來完成,後者接受在類 Types
中所定義的 JDBC 型別作為其變數。程式設計師可以用ResultSet.getXXX
方法系列來檢索返回給ResultSet
物件的結果,用 CallableStatement.getXXX
方法系列來檢索存放在輸出引數中的值。
用於 ResultSet
.getXXX
方法的 XXX
型別在某些情況下非常靈活。8.6.6 節中所示的表顯示了哪些 ResultSet
.getXXX
方法可用於檢索哪些 JDBC 型別。
用於 CallableStatement
.getXXX
方法的 XXX
型別必須對映為那個引數所註冊的 JDBC 型別。例如,如果資料庫應返回型別為 JDBC
REAL
的輸出值,則該引數應被註冊為java.sql.Types.REAL
。因此,要檢索該 JDBC
REAL
值,必須呼叫 CallableStatement.getFloat
方法(從 JDBC 型別到 Java 型別的對映在 8.6.1 節中的表中給出)。方法 getFloat
先把儲存在輸出引數中的值從 JDBC REAL
型別轉換為 Java float
型別,然後將它返回。為了適應各種資料庫和使應用程式具有更高的可移植性,建議先檢索 ResultSet
物件中的值,再檢索輸出引數中的值。
下述程式碼示範的是呼叫名為 getTestData
的已儲存過程。它有兩個引數,且都是 INOUT 引數。首先, Connection
物件 con
將建立 CallableStatement
物件 cstmt
。然後,方法setByte
把第一個引數設定為 Java byte
型別,其值為 25
。驅動程式將把 25
轉換為 JDBC TINYINT
型別並將之送到資料庫中。方法 setBigDecimal
用輸入值 83.75
來設定第二個引數。驅動程式將把這個 java.math.BigDecimal
物件轉換為 JDBC NUMERIC
值。接下來將這兩個引數註冊為 OUT 引數,第一個引數註冊為 JDBC TINYINT
型別,第二個引數註冊為小數點後面帶兩位數字的 JDBC DECIMAL
型別。執行 cstmt
後,就用 ResultSet.getXXX
方法將值從 ResultSet
物件中檢索出來。方法 getString
將第一列中的值作為 Java String
物件獲取,getInt
將第二列中的值作為 Java int
獲取,getInt
將第三列中的值作為 Java int
獲取。
之後, CallableStatement.getXXX
方法檢索存放在輸出引數中的值。方法 getByte
將 JDBC
TINYINT
檢索為 Java byte
,getBigDecimal
將 JDBC
DECIMAL
檢索為小數點後面帶有兩位數字的 java.math.BigDecimal
物件。注意,當引數既是輸入引數同時又是輸出引數時,setXXX
方法所用的 Java 型別與 getXXX
方法所用的相同(正如 setByte
和 getByte
中一樣)。registerOutParameter
方法將它註冊成由 Java 型別對映來的 JDBC 型別(Java byte
型別對映為 JDBC TINYINT
,如 8.6.2 節中的表所示)。
CallableStatement cstmt = con.prepareCall( "{call getTestData(?, ?)}"); cstmt.setByte(1, 25); cstmt.setBigDecimal(2, 83.75); // 將第一個引數註冊為 JDBC TINYINT,第二個 // 引數註冊為小數點後面帶有兩位數字的 JDBC DECIMAL 型別 cstmt.registerOutParameter(1, java.sql.Types.TINYINT); cstmt.registerOutParameter(2, java.sql.Types.DECIMAL, 2); ResultSet rs = cstmt.executeUpdate(); // 檢索並列印結果中的值。 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); // 檢索輸出引數中的值 byte x = cstmt.getByte(1); java.math.BigDecimal n = cstmt.getBigDecimal(2, 2);
總之,CallableStatement.getXXX
和 PreparedStatement.setXXX
方法系列中的 XXX
是 Java 型別。對於 setXXX
方法,驅動程式先把 Java 型別轉換為 JDBC 型別,再把它送到資料庫中(使用 8.6.2 節中的表所示的標準對映)。對於 getXXX
方法, 驅動程式先把資料庫返回的 JDBC 型別轉換為 Java 型別(用 8.6.1 節表中所示的標準對映),再把它返回給 getXXX
方法。
registerOutParameter
方法只接受 JDBC 型別的變數,而 setObject
方法卻可接受 JDBC 型別的變數。
注意,如果在可選的第三個變數的位置上提供了 JDBC 型別,則 setObject
方法將把引數值從 Java 型別顯式地轉換為所指定的 JDBC 型別。如果沒有為 setObject
提供目標 JDBC 型別,則將把引數值轉換為 Java 型別的標準對映 JDBC 型別(如 8.6.2 節的表中所示)。在將引數送到資料庫中之前,驅動程式都要進行顯式或隱式轉換。
8.5 動態資料存取
大多數時候,使用者要存取的結果和引數其資料型別在編譯時是已知的。然而,有些應用程式(例如普通的瀏覽器或查詢工具)在編譯時對它們所要存取的資料庫的機制並不知曉。因此,JDBC 除了支援靜態資料型別存取外,還支援型別完全動態確定的資料存取。
有三種方法和一個常量可用於訪問那些在編譯時其資料型別尚屬未知的值:
ResultSet.getObject
PreparedStatement.setObject
CallableStatement.getObject
java.sql.Types.OTHER
(用作CallableStatement.registerOutParameter
的一個變數)
例如,如果應用程式想要接受多種型別作為其 ResultSet
物件中的結果,它可以使用 ResultSet.getObject
方法。
ResultSet.getObject
和 CallableStatement.getObject
方法將值檢索為 Java Object
。由於 Object
是所有 Java 物件的基本類,因此可將任何 Java 類的例項檢索為 Object
的例項。然而,以下 Java 型別是內建的“基本”型別,因此,它們不是類 Object
的例項: boolean
、char
、byte
、short
、int
、long
、 float
和 double
。因此,不能用 getObject
方法來檢索它們。然而,這些基本型別每種都有相應的可用作 wrapper 的類。這些類的例項是物件,這意味著可用 ResultSet.getObject
和 CallableStatement.getObject
方法來檢索它們。第 67 頁中的表 8.6.3 顯示了從 JDBC 型別到 Java Object
型別的對映。該表與 JDBC 型別到 Java 型別的標準對映不同:在該表中,除了 JDBC TINYINT
和 JDBC SMALLINT
型別對映為 Java 類Integer
之外,每一個基本的 Java 型別都被替換為它們的 wrapper 類。
方法 getObject
還可用於檢索使用者定義的 Java 型別。隨著抽象資料型別(ADT)和其它使用者定義的型別在某些資料庫系統中的出現,一些提供者可能會發現用 getObject
來檢索這些型別將更方便。
8.6 資料型別對映表
本節含有以下表,它們是 JDBC 型別 和 Java 資料型別之間的對映關係表:
8.6.1 節 — 從 JDBC 型別對映到 Java 型別
8.6.2 節 — 從 Java 型別對映到 JDBC 型別
8.6.3 節 ─ 從 JDBC 型別對映到 Java Object
型別
8.6.4 節 ─ 從 Java Object
型別對映到 JDBC 型別
8.6.5 節 ─ 由 setObject
所進行的轉換
8.6.6 節 — 由 ResultSet.getXXX
方法所檢索的 JDBC 型別
8.6.1 從 JDBC 型別對映到 Java 型別
JDBC 型別 | Java 型別 |
---|---|
CHAR |
String |
VARCHAR |
String |
LONGVARCHAR |
String |
NUMERIC |
java.math.BigDecimal |
DECIMAL |
java.math.BigDecimal |
BIT |
boolean |
TINYINT |
byte |
SMALLINT |
short |
INTEGER |
int |
BIGINT |
long |
REAL |
float |
FLOAT |
double |
DOUBLE |
double |
BINARY |
byte[] |
VARBINARY |
byte[] |
LONGVARBINARY |
byte[] |
DATE |
java.sql.Date |
TIME |
java.sql.Time |
TIMESTAMP |
java.sql.Timestamp |
8.6.2 從 Java 型別對映到 JDBC 型別
該表顯示的是表 8.6.1 的反對映:Java 型別到 JDBC 型別的對映。
Java 型別 | JDBC 型別 |
---|---|
String |
VARCHAR 或 LONGVARCHAR |
java.math.BigDecimal |
NUMERIC |
boolean |
BIT |
byte |
TINYINT |
short |
SMALLINT |
int |
INTEGER |
long |
BIGINT |
float |
REAL |
double |
DOUBLE |
byte[] |
VARBINARY 或 LONGVARBINARY |
java.sql.Date |
DATE |
java.sql.Time |
TIME |
java.sql.Timestamp |
TIMESTAMP |
String 型別的對映通常是 VARCHAR
,但如果所給的值超出了驅動程式對 VARCHAR
值所限定的極限,則將轉換為 LONGVARCHAR
型別。對 byte[]
、VARBINARY
及 LONGVARBINARY
值也一樣。
8.6.3 從 JDBC 型別到 Java Object 型別的對映
由於 Java 內建型別(例如 boolean
和 int
)不是 Object
的子型別,因此對於 getObject
/setObject
方法,從 JDBC 型別到 Java object 型別的對映稍有不同。此種對映如下表所示:
JDBC 型別 | Java Object 型別 |
---|---|
CHAR |
String |
VARCHAR |
String |
LONGVARCHAR |
String |
NUMERIC |
java.math.BigDecimal |
DECIMAL |
java.math.BigDecimal |
BIT |
Boolean |
TINYINT |
Integer |
SMALLINT |
Integer |
INTEGER |
Integer |
BIGINT |
Long |
REAL |
Float |
FLOAT |
Double |
DOUBLE |
Double |
BINARY |
byte[] |
VARBINARY |
byte[] |
LONGVARBINARY |
byte[] |
DATE |
java.sql.Date |
TIME |
java.sql.Time |
TIMESTAMP |
java.sql.Timestamp |
8.6.4 Java Object 型別對映到 JDBC 型別
Java Object 型別 | JDBC 型別 |
---|---|
String |
VARCHAR 或 LONGVARCHAR |
java.math.BigDecimal |
NUMERIC |
Boolean |
BIT |
Integer |
INTEGER |
Long |
BIGINT |
Float |
REAL |
Double |
DOUBLE |
byte[] |
VARBINARY 或 LONGVARBINARY |
java.sql.Date |
DATE |
java.sql.Time |
TIME |
java.sql.Timestamp |
TIMESTAMP |
注意,String
的對映通常為 VARCHAR
,但如果所給的值超出了驅動程式對 VARCHAR
值所限定的極限值,則將轉換為 LONGVARCHAR
。對 byte[]
、VARBINARY
和 LONGVARBINARY
值也一樣。
8.6.5 由 setObject 所進行的轉換
setObject
T I N Y I N T | S M A L L I N T | I N T E G E R | B I G I N T | R E A L | F L O A T | D O U B L E | D E C I M A L | N U M E R I C | B I T | C H A R | V A R C H A R | L O N G V A R C H A R | B I N A R Y | V A R B I N A R Y | L O N G V A R B I N A R Y | D A T E | T I M E | T I M E S T A M P | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
String | x | x | x | x | x | x | x | x | x | x | x | x | x | x | x | x | x | x | x |
java.math.BigDecimal | x | x | x | x | x | x | x | x | x | x | x | x | x | ||||||
Boolean | x | x | x | x | x | x | x | x | x | x | x | x | x | ||||||
Integer | x | x | x | x | x | x | x | x | x | x | x | x | x | ||||||
Long | x | x | x | x | x | x | x | x | x | x | x | x | x | ||||||
Float | x | x | x | x | x | x | x | x | x | x | x | x | x | ||||||
Double | x | x | x | x | x | x | x | x | x | x | x | x | x | ||||||
byte[] | x | x | x | ||||||||||||||||
java.sql.Date | x | x | x | x | x | ||||||||||||||
java.sql.Time | x | x | x | x | |||||||||||||||
java.sql.Time- stamp | x | x | x | x | x | x |
從 Java object 型別到 JDBC 型別的轉換。
8.6.6 由 ResultSet.getXXX 方法檢索的 JDBC 型別
"x" 表示該方法可以檢索 JDBC 型別。"X" 表示建議使用該方法來檢索該 JDBC 型別。
T I N Y I N T | S M A L L I N T | I N T E G E R | B I G I N T | R E A L | F L O A T | D O U B L E | D E C I M A L | N U M E R I C | B I T | C H A R | V A R C H A R | L O N G V A R C H A R | B I N A R Y | V A R B I N A R Y | L O N G V A R B I N A R Y | D A T E | T I M E | T I M E S T A M P | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
getByte | X | x | x | x | x | x | x | x | x | x | x | x | x | ||||||
getShort | x | X | x | x | x | x | x | x | x | x | x | x | x | ||||||
getInt | x | x | X | x | x | x | x | x | x | x | x | x | x | ||||||
getLong | x | x | x | X | x | x | x | x | x | x | x | x | x | ||||||
getFloat | x | x | x | x | X | x | x | x | x | x | x | x | x | ||||||
getDouble | x | x | x | x | x | X | X | x | x | x | x | x | x | ||||||
getBigDecimal | x | x | x | x | x | x | x | X | X | x | x | x | x | ||||||
getBoolean | x | x | x | x | x | x | x | x | x | X | x | x | x | ||||||
getString | x | x | x | x | x | x | x | x | x | x | X | X | x | x | x | x | x | x | x |
getBytes | X | X | x | ||||||||||||||||
getDate | x | x | x | X | x | ||||||||||||||
getTime | x | x | x | X | x | ||||||||||||||
getTimestamp | x | x | x | x | X | ||||||||||||||
getAsciiStream | x | x | X | x | x | x | |||||||||||||
getUnicodeStream | x | x | X | x | x | x | |||||||||||||
getBinaryStream | x | x | X | ||||||||||||||||
getObject | x | x | x | x | x | x | x | x | x | x | x | x | x | x | x | x | x | x | x |