天天看點

PreparedStatement與Statement

PreparedStatement是用來運作SQL查詢語句的API之中的一個,Java提供了 Statement、PreparedStatement 和 CallableStatement三種方式來運作查詢語句,當中 Statement 用于通用查詢。 PreparedStatement 用于運作參數化查詢,而 CallableStatement則是用于存儲過程。

同一時候PreparedStatement還常常會在Java面試被提及,譬如:Statement與PreparedStatement的差别以及怎樣避免SQL注入式攻擊?這篇教程中我們會讨論為什麼要用PreparedStatement?使用PreparedStatement有什麼樣的優勢?PreparedStatement又是怎樣避免SQL注入攻擊的?

PreparedStatement是什麼?

PreparedStatement是java.sql包以下的一個接口。用來運作SQL語句查詢,通過調用connection.preparedStatement(sql)方法能夠獲得PreparedStatment對象。資料庫系統會對sql語句進行預編譯處理(假設JDBC驅動支援的話)。預處理語句将被預先編譯好,這條預編譯的sql查詢語句能在将來的查詢中重用,這樣一來。它比Statement對象生成的查詢速度更快。以下是一個樣例:

這個樣例中,假設還是用 PreparedStatement 做相同的查詢,哪怕參數值不一樣,比方:”Standard Chated” 或者”HSBC”作為參數值,資料庫系統還是會去調用之前編譯器編譯好的運作語句(系統庫系統初次會對查詢語句做最大的性能優化)。預設會傳回”TYPE_FORWARD_ONLY”類型的結果集( ResultSet ),當然你也能夠使用preparedstatment()的重載方法傳回不同類型的結果集。

預處理語句的優勢

PreparedStatement提供了諸多優點,企業級應用開發中強烈推薦使用PreparedStatement來做SQL查詢。以下列出PreparedStatement幾點優勢:

PreparedStatement能夠寫動态參數化的查詢

用PreparedStatement你能夠寫帶參數的sql查詢語句,通過使用相同的sql語句和不同的參數值來做查詢比建立一個不同的查詢語句要好,以下是一個參數化查詢:

SELECT interest_rate FROM loan WHERE loan_type=?

如今你能夠使用不論什麼一種loan類型如:”personal loan”,”home loan” 或者”gold loan”來查詢。這個樣例叫做參數化查詢,由于它能夠用不同的參數調用它,這裡的”?

”就是參數的占位符。

PreparedStatement比 Statement 更快

使用 PreparedStatement 最重要的一點優點是它擁有更佳的性能優勢,SQL語句會預編譯在資料庫系統中。

運作計劃相同會被緩存起來,它同意資料庫做參數化查詢。使用預處理語句比普通的查詢更快,由于它做的工作更少(資料庫對SQL語句的分析,編譯,優化已經在第一次查詢前完畢了)。為了降低資料庫的負載,生産環境中德JDBC代碼你應該總是使用PreparedStatement 。值得注意的一點是:為了獲得性能上的優勢。應該使用參數化sql查詢而不是字元串追加的方式。以下兩個SELECT 查詢,第一個SELECT查詢就沒有不論什麼性能優勢。

SQL Query 1:字元串追加形式的PreparedStatement

String loanType = getLoanType();

PreparedStatement prestmt = conn.prepareStatement("select banks from loan where loan_type=" + loanType);

SQL Query 2:使用參數化查詢的PreparedStatement

PreparedStatement prestmt = conn.prepareStatement("select banks from loan where loan_type=?

");

prestmt.setString(1,loanType);

第二個查詢就是正确使用PreparedStatement的查詢,它比SQL1能獲得更好的性能。

PreparedStatement能夠防止SQL注入式攻擊

假設你是做Java web應用開發的,那麼必須熟悉那聲名狼藉的SQL注入式攻擊。去年Sony就遭受了SQL注入攻擊,被盜用了一些Sony play station(PS機)使用者的資料。在SQL注入攻擊裡,惡意使用者通過SQL中繼資料綁定輸入,比方:某個站點的登入驗證SQL查詢代碼為:

strSQL = "SELECT * FROM users WHERE name = '" + userName + "' and pw = '"+ passWord +"';"

惡意填入:

userName = "1' OR '1'='1";

passWord = "1' OR '1'='1";

那麼終于SQL語句變成了:

strSQL = "SELECT * FROM users WHERE name = '1' OR '1'='1' and pw = '1' OR '1'='1';"

由于WHERE條件恒為真,這就相當于運作:

strSQL = "SELECT * FROM users;"

是以能夠達到無賬号password亦可登入站點。

假設惡意使用者要是更壞一點,使用者填入:

SQL語句變成了:

strSQL = "SELECT * FROM users WHERE name = 'any_value' and pw = ''; DROP TABLE users"

這樣一來。雖然沒有登入,可是資料表都被删除了。

然而使用PreparedStatement的參數化的查詢能夠阻止大部分的SQL注入。

在使用參數化查詢的情況下,資料庫系統(eg:MySQL)不會将參數的内容視為SQL指令的一部分來處理,而是在資料庫完畢SQL指令的編譯後。才套用參數運作。是以就算參數中含有破壞性的指令,也不會被資料庫所運作。

補充:避免SQL注入的另外一種方式:

在組合SQL字元串的時候,先對所傳入的參數做字元代替(将單引號字元代替為連續2個單引號字元,由于連續2個單引號字元在SQL資料庫中會視為字元中的一個單引號字元,譬如:

strSQL = "SELECT * FROM users WHERE name = '" + userName + "';"

傳入字元串:

userName  = " 1' OR 1=1 "

把userName做字元替換後變成:

userName = " 1'' OR 1=1"

最後生成的SQL查詢語句為:

strSQL = "SELECT * FROM users WHERE name = '1'' OR 1=1'

這樣資料庫就會去系統查找name為“1′ ‘ OR 1=1”的記錄。而避免了SQL注入。

比起淩亂的字元串追加似的查詢,PreparedStatement查詢可讀性更好、更安全。

PreparedStatement的局限性

雖然PreparedStatement很有用。可是它仍有一定的限制。

1. 為了防止SQL注入攻擊。PreparedStatement不同意一個占位符(?)有多個值。在運作有**IN**子句查詢的時候這個問題變得棘手起來。以下這個SQL查詢使用PreparedStatement就不會傳回不論什麼結果

SELECT * FROM loan WHERE loan_type IN (?

)

preparedSatement.setString(1, "'personal loan', 'home loan', 'gold loan'");

那怎樣解決問題呢?請你繼續關注本部落格。下期告訴你答案。

不算總結的總結

關于PreparedStatement接口,須要重點記住的是:

1. PreparedStatement能夠寫參數化查詢,比Statement能獲得更好的性能。

2. 對于PreparedStatement來說,資料庫能夠使用已經編譯過及定義好的運作計劃。這樣的預處理語句查詢比普通的查詢運作速度更快。

3. PreparedStatement能夠阻止常見的SQL注入式攻擊。

4. PreparedStatement能夠寫動态查詢語句

5. PreparedStatement與java.sql.Connection對象是關聯的,一旦你關閉了connection,PreparedStatement也沒法使用了。

6. “?” 叫做占位符。

7. PreparedStatement查詢預設傳回FORWARD_ONLY的ResultSet,你僅僅能往一個方向移動結果集的遊标。

當然你還能夠設定為其它類型的值如:”CONCUR_READ_ONLY”。

8. 不支援預編譯SQL查詢的JDBC驅動,在調用connection.prepareStatement(sql)的時候。它不會把SQL查詢語句發送給資料庫做預處理,而是等到運作查詢動作的時候(調用executeQuery()方法時)才把查詢語句發送個資料庫,這樣的情況和使用Statement是一樣的。

9. 占位符的索引位置從1開始而不是0,假設填入0會導緻*java.sql.SQLException invalid column index*異常。

是以假設PreparedStatement有兩個占位符。那麼第一個參數的索引時1,第二個參數的索引是2.

以上就是為什麼要使用PreparedStatement的所有理由,隻是你仍然能夠使用Statement對象用來做做測試。可是在生産環境下你一定要考慮使用 PreparedStatement 。

本文轉自mfrbuaa部落格園部落格,原文連結:http://www.cnblogs.com/mfrbuaa/p/5112326.html,如需轉載請自行聯系原作者