PreparedStatement
發(fā)表時間:2024-02-22 來源:明輝站整理相關(guān)軟件相關(guān)文章人氣:
[摘要]From: www.theserverside.com -->SMTH翻譯: SuperMMX 為什么 PreparedStatement 很重要, 以及怎樣"正確"使用他們. 數(shù)據(jù)庫有一個艱苦的工作. 它們不斷地從許多客戶端讀取 SQL 查詢, 對數(shù)據(jù)進行盡 可能高效...
From: www.theserverside.com -->SMTH
翻譯: SuperMMX
為什么 PreparedStatement 很重要, 以及怎樣"正確"使用他們.
數(shù)據(jù)庫有一個艱苦的工作. 它們不斷地從許多客戶端讀取 SQL 查詢, 對數(shù)據(jù)進行盡
可能高效的 查詢. 處理語句可能成為一個代價較高的操作, 但是現(xiàn)在數(shù)據(jù)庫都是很
好的設(shè)計, 這樣這個困難 被減到最小. 但是這些優(yōu)化需要應(yīng)用程序開發(fā)者的協(xié)助,
這篇文章給你展示一下怎樣正確使用 PreparedStatement 來漂亮地幫助數(shù)據(jù)庫執(zhí)行
這些優(yōu)化.
一個數(shù)據(jù)庫怎樣執(zhí)行一條語句?
顯然, 不要希望這里有許多細節(jié); 我們只看一下對這篇文章比較重要的部分. 當(dāng)一個
數(shù)據(jù)庫接收 到一條語句的時候, 數(shù)據(jù)庫引擎首先解析這條語句, 查看語法錯誤. 一
旦語句解析了, 數(shù)據(jù)庫 需要找出最有效的方法來執(zhí)行這條語句. 這個計算起來代價
很大. 數(shù)據(jù)庫檢查什么索引(如果有 的話)能有所幫助, 或者它是否能全部讀出一張
表中所有的記錄. 數(shù)據(jù)庫根據(jù)這些關(guān)于數(shù)據(jù)庫所 存數(shù)據(jù)的統(tǒng)計數(shù)字來找出最好的
辦法. 一旦制訂出查詢方案, 就可以由數(shù)據(jù)庫引擎來執(zhí)行.
需要 CPU 來產(chǎn)生訪問方案. 理想的情況, 如果我們把相同的語句給數(shù)據(jù)庫發(fā)送兩
次, 我們期望 數(shù)據(jù)庫重用第一條記錄的訪問方案. 這會比第二次重新產(chǎn)生方案要使
用較少的 CPU.
語句緩沖
數(shù)據(jù)庫可以進行調(diào)節(jié)來做語句緩沖. 通常包含一些類型的語句緩沖. 緩沖使用語句
本身作為關(guān)鍵 字, 訪問方案和相應(yīng)的語句存儲在緩沖區(qū)中. 這樣就允許數(shù)據(jù)庫引擎
對以前執(zhí)行過的語句所使用 的訪問方案進行重用. 舉個例子來說, 如果我們向數(shù)據(jù)
庫發(fā)送這樣一條語句 "select a, b from t where c = 2", 計算好的訪問方案就放入緩沖
區(qū)了. 如果我們以后再使用同樣的語 句, 數(shù)據(jù)庫就能重用以前的訪問方案, 這樣就
能節(jié)省 CPU.
但是要注意, 整條語句是一個關(guān)鍵字. 例如, 如果我們后來發(fā)送的語句是 "select a,b
from t where c = 3", 那么就不會找出以前的訪問方案. 因為 "c=3" 和 "c=2" 是不一
樣的. 所以, 例如:
For(int I = 0; I < 1000; ++I)
{
PreparedStatement ps = conn.prepareStatement("select a,b from t
where c = " + I);
ResultSet rs = Ps.executeQuery();
Rs.close();
Ps.close();
}
這里不會用到緩沖. 每次循環(huán)向數(shù)據(jù)庫發(fā)送一條不同的 SQL 語句. 每次循環(huán)都重新
計算新的訪問 方案, 用這種方法我們會浪費大量的 CPU 周期. 但是, 看看下一個片
段:
PreparedStatement ps = conn.prepareStatement("select a,b from t where c
= ?");
For(int I = 0; I < 1000; ++I)
{
ps.setInt(1, I);
ResultSet rs = ps.executeQuery();
Rs.close();
}
ps.close();
這樣就會高效得多. 發(fā)送給數(shù)據(jù)庫的語句在 sql 中使用 '?' 符號來參數(shù)化. 這意味著
每次循環(huán) 發(fā)送的是同一條語句, 在 "c=?" 部分帶有不同的參數(shù). 這樣就允許數(shù)據(jù)庫
重用語句的訪問方案, 是程序在數(shù)據(jù)庫內(nèi)部運行得更高效. 這基本上能使你的程序
運行得更快, 或者使數(shù)據(jù)庫用戶能更多 地使用 CPU.
PreparedStatement 和 J2EE 服務(wù)器
當(dāng)我們使用 J2EE 服務(wù)器的時候, 事情會變得更加復(fù)雜. 通常情況下, 一個預(yù)先準(zhǔn)備
好的語句 (prepared statement) 是和一個單獨的數(shù)據(jù)庫連接相關(guān)聯(lián)的. 當(dāng)連接關(guān)閉時,
語句就被丟棄 了. 一般來說, 一個胖客戶端應(yīng)用程序在得到一個數(shù)據(jù)庫連接后會一
直保持到程序結(jié)束. 它會使用 兩種方法創(chuàng)建所有的語句: 急切創(chuàng)建(eagerly) 或者 懶
惰創(chuàng)建(lazily). Eagerly是說, 當(dāng)程序啟動時全部創(chuàng)建. Lazily是說隨用隨創(chuàng)建. 急切
的方法會在程序啟動時有些延時, 但是一旦程序啟動以后, 運行很好. 懶惰的方法啟
動很快, 但是當(dāng)程序運行時, 預(yù)先準(zhǔn)備的語句在第一次使用是創(chuàng)建. 這就會造成性能
不平衡, 知道所有的 語句都準(zhǔn)備好了, 但是最終程序會和急切方法一樣快. 哪一種
最好要看你需要的是快速啟動還是 均衡的性能.
一個 J2EE 應(yīng)用程序所帶來的問題就是它不能像這樣工作. 它只在一個請求的生存
時間中保持一個 連接. 這意味著在他處理每一個請求時都會重新創(chuàng)建語句, 就不象
胖客戶端只創(chuàng)建一次, 而不是每 個請求都創(chuàng)建那樣有效,
當(dāng) J2EE 服務(wù)器給你的程序一個連接時, 并不是一個真正的連接, 而是一個經(jīng)過包裝
的. 你可以 通過查看那個連接的類的名字來檢驗一下. 它不是一個數(shù)據(jù)庫的 JDBC
連接, 是你的服務(wù)器創(chuàng)建 的一個類. 通常, 如果你調(diào)用一個連接的 close 方法, 那么
jdbc 驅(qū)動程序會關(guān)閉這個連接. 我們希望的是當(dāng) J2EE 應(yīng)用程序調(diào)用 close 的時候,
連接會返回到連接池中. 我們通過設(shè)計一個 代理的 jdbc 連接類來做這些, 但看起來
就象是實際的連接. 當(dāng)我們調(diào)用這個連接的任何方法時, 代理類就會把請求前遞給
實際的連接. 但是, 當(dāng)我們調(diào)用類似 close 的方法時, 并不調(diào)用實際 連接的 close 方
法, 只是簡單地把連接返回給連接池, 然后把代理連接標(biāo)記為無效, 這樣當(dāng)它 被應(yīng)
用程序重新使用時, 我們會得到異常.
包裝是非常有用的, 因為它幫助 J2EE 應(yīng)用程序服務(wù)器實現(xiàn)者比較聰明地加上預(yù)先
準(zhǔn)備語句的 支持. 當(dāng)程序調(diào)用 Connection.prepareStatement 時, 由驅(qū)動程序返回一
個 PreparedStatement 對象. 當(dāng)應(yīng)用程序得到它時, 保存這個句柄, 并且在請求完成
時, 關(guān)閉 請求之前關(guān)閉這個句柄. 但是, 在連接返回到連接池之后, 以后被同樣或者
另一個應(yīng)用程序重用時, 那么, 我們就理論上希望同樣的 PreparedStatement 返回給
應(yīng)用程序.
J2EE PreparedStatement 緩沖
J2EE PreparedStatement 緩沖由 J2EE 服務(wù)器內(nèi)部的連接池管理器使用一個緩沖區(qū)
來 實現(xiàn). J2EE 服務(wù)器在連接池中保存一個所有數(shù)據(jù)庫的預(yù)先準(zhǔn)備語句的一個列表.
當(dāng)一個程序 調(diào)用一個連接的 prepareStatement 方法時, 服務(wù)器先檢查這個語句是否
已經(jīng)有了, 如果 是, 相應(yīng)的 PreparedStatement 就在緩沖區(qū)內(nèi), 就返回給應(yīng)用程序, 如
果不是, 請求就 會傳遞給 jdbc 驅(qū)動程序, 請求/預(yù)先準(zhǔn)備語句 對象就會加入到緩沖
區(qū)里.
對于每一個連接我們需要一個緩沖區(qū), 因為這是 jdbc 驅(qū)動程序的工作要求. 任何返
回的 preparedStatement 都是針對這個連接的.
如果我們要利用緩沖區(qū)的優(yōu)勢, 要使用和前面相同的規(guī)則. 我們需要使用參數(shù)話的
查詢, 這樣 它們就會和已經(jīng)在緩沖區(qū)的某一個匹配. 大多數(shù)應(yīng)用程序服務(wù)器都允許
你調(diào)整緩沖區(qū)的大小.
概要
總之, 對于預(yù)先準(zhǔn)備語句, 我們應(yīng)該使用參數(shù)化的查詢. 這樣允許數(shù)據(jù)庫重用已經(jīng)存
在的訪問 方案, 從而減輕數(shù)據(jù)庫的負(fù)擔(dān). 這樣的緩沖區(qū)是這個數(shù)據(jù)庫范圍的, 所以
你可以安排你所有的 應(yīng)用程序, 使用相似的參數(shù)化的 SQL, 就會提高這樣的緩沖區(qū)
方案的效率, 因為一個應(yīng)用程序 可以使用另一個應(yīng)用程序的語句. 一個應(yīng)用服務(wù)器
的優(yōu)勢也在于此, 因為訪問數(shù)據(jù)庫的邏輯應(yīng)該 集中在數(shù)據(jù)訪問層上(OR 映射, 實體
bean 或者直接 JDBC).
最后, 預(yù)先準(zhǔn)備語句的正確使用也讓你利用應(yīng)用程序服務(wù)器的預(yù)先準(zhǔn)備語句的緩沖
區(qū)的好處. 會提高你的應(yīng)用程序的性能, 因為應(yīng)用程序通過對以前的預(yù)先準(zhǔn)備語句
的重用減少 JDBC 驅(qū)動程序調(diào)用的次數(shù). 這樣使它能和胖客戶端的效率競爭, 并且
去掉了不能保持一個長期 連接的壞處.
如果你使用參數(shù)化的預(yù)先準(zhǔn)備語句, 就可以提高數(shù)據(jù)庫和你的服務(wù)器端的代碼的效
率. 這些提高 都會允許你的應(yīng)用程序提高性能.