JDBC-Statement,prepareStatement,CallableStatement的比較


一、Connection[接口]

1. 打開一個連接

創建與數據庫的連接的標准方式是在DataSource或DriverManager上調用方法getConnection()。Driver方法connect使用URL來建立連接。

用戶可以設置JDBC管理層,然后直接調用Driver方法。在兩個驅動程序連接到一個數據庫,而用戶想明確地選擇一個特定的驅動程序時,這是很有用的,盡管這種情況很少見。然而,通常讓DataSource或DriverManager打開連接會更容易。

 

2. 數據庫URL

URL(Uniform Resource Locator,統一資源定位符)是在Intenet查找資源的標識符。可以認為它是一個地址。JDBC URL是確定數據庫的靈活方式,從而使得適當的驅動程序識別它並建立與它的連接。JDBC URL允許不同的驅動程序為命名數據庫使用不同的方案。例如,odbc子協議允許URL包含屬性值。

jdbc:<subprotocol>:<subname>

JDBC URL的三個部分如下:

jdbc:協議。JDBC URL中的協議總是jdbc。

<subprotocol>:驅動程序或連接機制的名稱,可以有一個或多個驅動程序支持。

<subname>:數據庫的唯一標識符。

 

3. odbc子協議

odbc子協議特性是,允許在數據庫名稱后指定任意數量的屬性值,如下所示:

jdbc:odbc:<data-source-name>[;<attribute-name>=<attribute-value>]*]

 

二、SQL語句

JDBC核心API提供了三種向數據庫發送SQL語句的類:

  • Statement:使用createStatement()創建;
  • PreparedStatement:經過預編譯並存儲在PreparedStatement對象中的SQL語句,使用prepareStatement()方法創建。
  • CallableStatement:用於執行SQL存儲過程,使用prepareCall()方法創建。

1. Statement對象 

Statement對象用於執行靜態SQL語句和獲得SQL產生的結果。定義了三種執行SQL語句的方法,用來處理返回不同結果的SQL命令:

  • executeUpdate(String sql):執行SQL INSERT,UPDATE或DELETE語句,返回受影響行的數目或零;
    返回值為int型
  • executeQuery(String sql):執行返回單個ResultSet的SQL語句;
    返回類型ResultSet
  • execute(String sql):執行可以返回多個結果的SQL語句。
    返回類型為boolean,如果返回的是更新的數目,則返回false,如果返回ResultSet,則返回true。

2. PreparedStatement語句

PreparedStatement是java.sql包下面的一個接口,用來執行SQL語句查詢,通過調用connection.preparedStatement(sql)方法可以獲得PreparedStatment對象。

數據庫系統會對sql語句進行預編譯處理(如果JDBC驅動支持的話),預處理語句將被預先編譯好,這條預編譯的sql查詢語句能在將來的查詢中重用,這樣一來,它比Statement對象生成的查詢速度更快。

PreparedStatement僅僅是預編譯語句。可以使用占位符。

下面是一個例子:

 

 1 public class PreparedStmtExample {
 2   
 3     public static void main(String args[]) throws SQLException {
 4         Connection conn = DriverManager.getConnection("mysql:\\localhost:1520", "root", "root");
 5         PreparedStatement preStatement = conn.prepareStatement("select distinct loan_type from loan where bank=?");
 6         preStatement.setString(1, "Citibank");
 7   
 8         ResultSet result = preStatement.executeQuery();
 9   
10         while(result.next()){
11             System.out.println("Loan Type: " + result.getString("loan_type"));
12         }       
13     }
14 } 
15 Output:
16 Loan Type: Personal Loan
17 Loan Type: Auto Loan
18 Loan Type: Home Loan
19 Loan Type: Gold Loan

 

這個例子中,如果還是用 PreparedStatement 做同樣的查詢,哪怕參數值不一樣,比如:”Standard Chated” 或者”HSBC”作為參數值,數據庫系統還是會去調用之前編譯器編譯好的執行語句(系統庫系統初次會對查詢語句做最大的性能優化)。

默認會返回”TYPE_FORWARD_ONLY”類型的結果集( ResultSet ),當然你也可以使用preparedstatment()的重載方法返回不同類型的結果集。

三、預處理語句的優勢

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

 

1)PreparedStatement可以寫動態參數化的查詢

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

SELECT interest_rate FROM loan WHERE loan_type=?

 

現在你可以使用任何一種loan類型如:”personal loan”,”home loan” 或者”gold loan”來查詢,這個例子叫做參數化查詢,因為它可以用不同的參數調用它,這里的”?”就是參數的占位符。 

2)PreparedStatement比 Statement 更快

使用 PreparedStatement 最重要的一點好處是它擁有更佳的性能優勢,SQL語句會預編譯在數據庫系統中。執行計划同樣會被緩存起來,它允許數據庫做參數化查詢。使用預處理語句比普通的查詢更快,因為它做的工作更少(數據庫對SQL語句的分析,編譯,優化已經在第一次查詢前完成了)。為了減少數據庫的負載,生產環境中德JDBC代碼你應該總是使用PreparedStatement 。值得注意的一點是:為了獲得性能上的優勢,應該使用參數化sql查詢而不是字符串追加的方式。下面兩個SELECT 查詢,第一個SELECT查詢就沒有任何性能優勢。

SQL Query 1:字符串追加形式的PreparedStatemen

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能獲得更好的性能。 

 

3)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 = "1' OR '1'='1";DROP TABLE USERS;

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'

 

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

 

四、PreparedStatement的局限性


盡管PreparedStatement非常實用,但是它仍有一定的限制。 
1. 為了防止SQL注入攻擊,PreparedStatement不允許一個占位符(?)有多個值,在執行有**IN**子句查詢的時候這個問題變得棘手起來。下面這個SQL查詢使用PreparedStatement就不會返回任何結果 

五、不算總結的總結

關於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 。

 

3. CallableStatement

允許從Java應用程序中調用數據庫存儲過程。CallableStatement對象包含了對存儲過程的調用;但不包含存儲過程本身,這是由於存儲過程是存儲在數據庫中的。

使用方法:CallableStatement cStmt = conn.prepareCall("{call 存儲過程名(參數表列)}");

 
 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM