4 - statement
本概述是從《jdbctm database access from javatm: a tutorial and annotated reference 》這本書中摘引來的。javasoft 目前正在準備這本書。這是一本教程,同時也是 jdbc 的重要參考手冊,它將作為 java 系列的組成部份在 1997 年春季由 addison-wesley 出版公司出版。
4.1 概述
statement 對象用于將 sql 語句發(fā)送到數(shù)據(jù)庫中。實際上有三種 statement 對象,它們都作為在給定連接上執(zhí)行 sql 語句的包容器:statement、preparedstatement(它從 statement 繼承而來)和 callablestatement(它從 preparedstatement 繼承而來)。它們都專用于發(fā)送特定類型的 sql 語句: statement 對象用于執(zhí)行不帶參數(shù)的簡單 sql 語句;preparedstatement 對象用于執(zhí)行帶或不帶 in 參數(shù)的預(yù)編譯 sql 語句;callablestatement 對象用于執(zhí)行對數(shù)據(jù)庫已存儲過程的調(diào)用。
statement 接口提供了執(zhí)行語句和獲取結(jié)果的基本方法。preparedstatement 接口添加了處理 in 參數(shù)的方法;而 callablestatement 添加了處理 out 參數(shù)的方法。
4.1.1 創(chuàng)建 statement 對象
建立了到特定數(shù)據(jù)庫的連接之后,就可用該連接發(fā)送 sql 語句。statement 對象用 connection 的方法 createstatement 創(chuàng)建,如下列代碼段中所示:
connection con = drivermanager.getconnection(url, "sunny", "");
statement stmt = con.createstatement();
為了執(zhí)行 statement 對象,被發(fā)送到數(shù)據(jù)庫的 sql 語句將被作為參數(shù)提供給 statement 的方法:
resultset rs = stmt.executequery("select a, b, c from table2");
4.1.2 使用 statement 對象執(zhí)行語句
statement 接口提供了三種執(zhí)行 sql 語句的方法:executequery、executeupdate 和 execute。使用哪一個方法由 sql 語句所產(chǎn)生的內(nèi)容決定。
方法 executequery 用于產(chǎn)生單個結(jié)果集的語句,例如 select 語句。
方法 executeupdate 用于執(zhí)行 insert、update 或 delete 語句以及 sql ddl(數(shù)據(jù)定義語言)語句,例如 create table 和 drop table。insert、update 或 delete 語句的效果是修改表中零行或多行中的一列或多列。executeupdate 的返回值是一個整數(shù),指示受影響的行數(shù)(即更新計數(shù))。對于 create table 或 drop table 等不操作行的語句,executeupdate 的返回值總為零。
方法 execute 用于執(zhí)行返回多個結(jié)果集、多個更新計數(shù)或二者組合的語句。因為多數(shù)程序員不會需要該高級功能,所以本概述后面將在單獨一節(jié)中對其進行介紹。
執(zhí)行語句的所有方法都將關(guān)閉所調(diào)用的 statement 對象的當(dāng)前打開結(jié)果集(如果存在)。這意味著在重新執(zhí)行 statement 對象之前,需要完成對當(dāng)前 resultset 對象的處理。
應(yīng)注意,繼承了 statement 接口中所有方法的 preparedstatement 接口都有自己的 executequery、executeupdate 和 execute 方法。statement 對象本身不包含 sql 語句,因而必須給 statement.execute 方法提供 sql 語句作為參數(shù)。preparedstatement 對象并不將 sql 語句作為參數(shù)提供給這些方法,因為它們已經(jīng)包含預(yù)編譯 sql 語句。callablestatement 對象繼承這些方法的 preparedstatement 形式。對于這些方法的 preparedstatement 或 callablestatement 版本,使用查詢參數(shù)將拋出 sqlexception。
4.1.3 語句完成
當(dāng)連接處于自動提交模式時,其中所執(zhí)行的語句在完成時將自動提交或還原。語句在已執(zhí)行且所有結(jié)果返回時,即認為已完成。對于返回一個結(jié)果集的 executequery 方法,在檢索完 resultset 對象的所有行時該語句完成。對于方法 executeupdate,當(dāng)它執(zhí)行時語句即完成。但在少數(shù)調(diào)用方法 execute 的情況中,在檢索所有結(jié)果集或它生成的更新計數(shù)之后語句才完成。
有些 dbms 將已存儲過程中的每條語句視為獨立的語句;而另外一些則將整個過程視為一個復(fù)合語句。在啟用自動提交時,這種差別就變得非常重要,因為它影響什么時候調(diào)用 commit 方法。在前一種情況中,每條語句單獨提交;在后一種情況中,所有語句同時提交。
4.1.4 關(guān)閉 statement 對象
statement 對象將由 java 垃圾收集程序自動關(guān)閉。而作為一種好的編程風(fēng)格,應(yīng)在不需要 statement 對象時顯式地關(guān)閉它們。這將立即釋放 dbms 資源,有助于避免潛在的內(nèi)存問題。
4.1.5 statement 對象中的 sql 轉(zhuǎn)義語法
statement 可包含使用 sql 轉(zhuǎn)義語法的 sql 語句。轉(zhuǎn)義語法告訴驅(qū)動程序其中的代碼應(yīng)該以不同方式處理。驅(qū)動程序?qū)呙枞魏无D(zhuǎn)義語法,并將它轉(zhuǎn)換成特定數(shù)據(jù)庫可理解的代碼。這使得轉(zhuǎn)義語法與 dbms 無關(guān),并允許程序員使用在沒有轉(zhuǎn)義語法時不可用的功能。
轉(zhuǎn)義子句由花括號和關(guān)鍵字界定:
{keyword . . . parameters . . . }
該關(guān)鍵字指示轉(zhuǎn)義子句的類型,如下所示。
escape 表示 like 轉(zhuǎn)義字符
字符“%”和“_”類似于 sql like 子句中的通配符(“%”匹配零個或多個字符,而“_”則匹配一個字符)。為了正確解釋它們,應(yīng)在其前面加上反斜杠(“/”),它是字符串中的特殊轉(zhuǎn)義字符。在查詢末尾包括如下語法即可指定用作轉(zhuǎn)義字符的字符:
{escape 'escape-character'}
例如,下列查詢使用反斜杠字符作為轉(zhuǎn)義字符,查找以下劃線開頭的標識符名:
stmt.executequery("select name from identifiers
where id like `/_%' {escape `/'};
fn 表示標量函數(shù)
幾乎所有 dbms 都具有標量值的數(shù)值、字符串、時間、日期、系統(tǒng)和轉(zhuǎn)換函數(shù)。要使用這些函數(shù),可使用如下轉(zhuǎn)義語法:關(guān)鍵字 fn 后跟所需的函數(shù)名及其參數(shù)。例如,下列代碼調(diào)用函數(shù) concat 將兩個參數(shù)連接在一起:
{fn concat("hot", "java")};
可用下列語法獲得當(dāng)前數(shù)據(jù)庫用戶名:
{fn user()};
標量函數(shù)可能由語法稍有不同的 dbms 支持,而它們可能不被所有驅(qū)動程序支持。各種 databasemetadata 方法將列出所支持的函數(shù)。例如,方法 getnumericfunctions 返回用逗號分隔的數(shù)值函數(shù)列表,而方法 getstringfunctions 將返回字符串函數(shù),等等。
驅(qū)動程序?qū)⑥D(zhuǎn)義函數(shù)調(diào)用映射為相應(yīng)的語法,或直接實現(xiàn)該函數(shù)。
d、t 和 ts 表示日期和時間文字
dbms 用于日期、時間和時間標記文字的語法各不相同。jdbc 使用轉(zhuǎn)義子句支持這些文字的語法的 iso 標準格式。驅(qū)動程序必須將轉(zhuǎn)義子句轉(zhuǎn)換成 dbms 表示。
例如,可用下列語法在 jdbc sql 語句中指定日期:
{d `yyyy-mm-dd'}
在該語法中,yyyy 為年代,mm 為月份,而 dd 則為日期。驅(qū)動程序?qū)⒂玫葍r的特定于 dbms 的表示替換這個轉(zhuǎn)義子句。例如,如果 '28- feb-99' 符合基本數(shù)據(jù)庫的格式,則驅(qū)動程序?qū)⒂盟鎿Q {d 1999-02-28}。
對于 time 和 timestamp 也有類似的轉(zhuǎn)義子句:
{t `hh:mm:ss'}
{ts `yyyy-mm-dd hh:mm:ss.f . . .'}
timestamp 中的小數(shù)點后的秒(.f . . .)部分可忽略。
call 或 ? = call 表示已存儲過程
如果數(shù)據(jù)庫支持已存儲過程,則可從 jdbc 中調(diào)用它們,語法為:
{call procedure_name[(?, ?, . . .)]}
或(其中過程返回結(jié)果參數(shù)):
{? = call procedure_name[(?, ?, . . .)]}
方括號指示其中的內(nèi)容是可選的。它們不是語法的必要部分。
輸入?yún)?shù)可以為文字或參數(shù)。有關(guān)詳細信息,參見 jdbc 指南中第 7 節(jié),“callablestatement”。
可通過調(diào)用方法 databasemetadata.supportsstoredprocedures 檢查數(shù)據(jù)庫是否支持已存儲過程。
oj 表示外部連接
外部連接的語法為
{oj outer-join}
其中 outer-join 形式為
table left outer join {table / outer-join} on search-condition
外部連接屬于高級功能。有關(guān)它們的解釋可參見 sql 語法。jdbc 提供了三種 databasemetadata 方法用于確定驅(qū)動程序支持哪些外部連接類型:supportsouterjoins、supportsfullouterjoins 和 supportslimitedouterjoins。
方法 statement.setescapeprocessing 可打開或關(guān)閉轉(zhuǎn)義處理;缺省狀態(tài)為打開。當(dāng)性能極為重要時,程序員可能想關(guān)閉它以減少處理時間。但通常它將出于打開狀態(tài)。應(yīng)注意: setescapeprocessing 不適用于 preparedstatement 對象,因為在調(diào)用該語句前它就可能已被發(fā)送到數(shù)據(jù)庫。有關(guān)預(yù)編譯的信息,參見 preparedstatement。
4.1.6 使用方法 execute
execute 方法應(yīng)該僅在語句能返回多個 resultset 對象、多個更新計數(shù)或 resultset 對象與更新計數(shù)的組合時使用。當(dāng)執(zhí)行某個已存儲過程或動態(tài)執(zhí)行未知 sql 字符串(即應(yīng)用程序程序員在編譯時未知)時,有可能出現(xiàn)多個結(jié)果的情況,盡管這種情況很少見。例如,用戶可能執(zhí)行一個已存儲過程(使用 callablestatement 對象 - 參見第 135 頁的 callablestatement),并且該已存儲過程可執(zhí)行更新,然后執(zhí)行選擇,再進行更新,再進行選擇,等等。通常使用已存儲過程的人應(yīng)知道它所返回的內(nèi)容。
因為方法 execute 處理非常規(guī)情況,所以獲取其結(jié)果需要一些特殊處理并不足為怪。例如,假定已知某個過程返回兩個結(jié)果集,則在使用方法 execute 執(zhí)行該過程后,必須調(diào)用方法 getresultset 獲得第一個結(jié)果集,然后調(diào)用適當(dāng)?shù)?getxxx 方法獲取其中的值。要獲得第二個結(jié)果集,需要先調(diào)用 getmoreresults 方法,然后再調(diào)用 getresultset 方法。如果已知某個過程返回兩個更新計數(shù),則首先調(diào)用方法 getupdatecount,然后調(diào)用 getmoreresults,并再次調(diào)用 getupdatecount。
對于不知道返回內(nèi)容,則情況更為復(fù)雜。如果結(jié)果是 resultset 對象,則方法 execute 返回 true;如果結(jié)果是 java int,則返回 false。如果返回 int,則意味著結(jié)果是更新計數(shù)或執(zhí)行的語句是 ddl 命令。在調(diào)用方法 execute 之后要做的第一件事情是調(diào)用 getresultset 或 getupdatecount。調(diào)用方法 getresultset 可以獲得兩個或多個 resultset 對象中第一個對象;或調(diào)用方法 getupdatecount 可以獲得兩個或多個更新計數(shù)中第一個更新計數(shù)的內(nèi)容。
當(dāng) sql 語句的結(jié)果不是結(jié)果集時,則方法 getresultset 將返回 null。這可能意味著結(jié)果是一個更新計數(shù)或沒有其它結(jié)果。在這種情況下,判斷 null 真正含義的唯一方法是調(diào)用方法 getupdatecount,它將返回一個整數(shù)。這個整數(shù)為調(diào)用語句所影響的行數(shù);如果為 -1 則表示結(jié)果是結(jié)果集或沒有結(jié)果。如果方法 getresultset 已返回 null(表示結(jié)果不是 resultset 對象),則返回值 -1 表示沒有其它結(jié)果。也就是說,當(dāng)下列條件為真時表示沒有結(jié)果(或沒有其它結(jié)果):
((stmt.getresultset() == null) && (stmt.getupdatecount() == -1))
如果已經(jīng)調(diào)用方法 getresultset 并處理了它返回的 resultset 對象,則有必要調(diào)用方法 getmoreresults 以確定是否有其它結(jié)果集或更新計數(shù)。如果 getmoreresults 返回 true,則需要再次調(diào)用 getresultset 來檢索下一個結(jié)果集。如上所述,如果 getresultset 返回 null,則需要調(diào)用 getupdatecount 來檢查 null 是表示結(jié)果為更新計數(shù)還是表示沒有其它結(jié)果。
當(dāng) getmoreresults 返回 false 時,它表示該 sql 語句返回一個更新計數(shù)或沒有其它結(jié)果。因此需要調(diào)用方法 getupdatecount 來檢查它是哪一種情況。在這種情況下,當(dāng)下列條件為真時表示沒有其它結(jié)果:
((stmt.getmoreresults() == false) && (stmt.getupdatecount() == -1))
下面的代碼演示了一種方法用來確認已訪問調(diào)用方法 execute 所產(chǎn)生的全部結(jié)果集和更新計數(shù):
stmt.execute(querystringwithunknownresults);
while (true) {
int rowcount = stmt.getupdatecount();
if (rowcount > 0) { // 它是更新計數(shù)
system.out.println("rows changed = " + count);
stmt.getmoreresults();
continue;
}
if (rowcount == 0) { // ddl 命令或 0 個更新
system.out.println(" no rows changed or statement was ddl
command");
stmt.getmoreresults();
continue;
}
// 執(zhí)行到這里,證明有一個結(jié)果集
// 或沒有其它結(jié)果
resultset rs = stmt.getresultset;
if (rs != null) {
. . . // 使用元數(shù)據(jù)獲得關(guān)于結(jié)果集列的信息
while (rs.next()) {
. . . // 處理結(jié)果
stmt.getmoreresults();
continue;
}
break; // 沒有其它結(jié)果
新聞熱點
疑難解答
圖片精選