JDBC之Statement,PreparedStatement,CallableStatement的區別

self_control發表於2015-01-30
  1. Statement 每次執行sql語句,資料庫都要執行sql語句的編譯 ,最好用於僅執行一次查詢並返回結果的情形,效率高於PreparedStatement.

     

    PreparedStatement是預編譯的,使用PreparedStatement有幾個好處

     a. 在執行可變引數的一條SQL時,PreparedStatement比Statement的效率高,因為DBMS預編譯一條SQL當然會比多次編譯一條SQL的效率要高。

     b. 安全性好,有效防止Sql注入等問題。

     c.  對於多次重複執行的語句,使用PreparedStament效率會更高一點,並且在這種情況下也比較適合使用batch;

     d.  程式碼的可讀性和可維護性。

     

    CallableStatement介面擴充套件 PreparedStatement,用來呼叫儲存過程,它提供了對輸出和輸入/輸出引數的支援。CallableStatement 介面還具有對 PreparedStatement 介面提供的輸入引數的支援。

     



    用法介紹

     

    statement 1:

    String updateString = "UPDATE COFFEES SET SALES = 75 " + "WHERE COF_NAME LIKE ′Colombian′";
    stmt.executeUpdate(updateString);

    preparedstatement 2:

    PreparedStatement updateSales = con.prepareStatement("UPDATE COFFEES SET SALES = ? WHERE COF_NAME LIKE ? ");
    updateSales.setInt(1, 75);
    updateSales.setString(2, "Colombian");
    updateSales.executeUpdate();

    片斷2和片斷1的區別在於,後者使用了PreparedStatement物件,而前者是普通的Statement物件。PreparedStatement物件不僅包含了SQL語句,而且大多數情況下這個語句已經被預編譯過,因而當其執行時,只需DBMS執行SQL語句,而不必先編譯。當你需要執行Statement物件多次的時候,PreparedStatement物件將會大大降低執行時間,當然也加快了訪問資料庫的速度。
    這種轉換也給你帶來很大的便利,不必重複SQL語句的句法,而只需更改其中變數的值,便可重新執行SQL語句。選擇PreparedStatement物件與否,在於相同句法的SQL語句是否執行了多次,而且兩次之間的差別僅僅是變數的不同。如果僅僅執行了一次的話,它應該和普通的物件毫無差異,體現不出它預編譯的優越性。
    5.執行許多SQL語句的JDBC程式產生大量的Statement和PreparedStatement物件。通常認為PreparedStatement物件比Statement物件更有效,特別是如果帶有不同引數的同一SQL語句被多次執行的時候。PreparedStatement物件允許資料庫預編譯SQL語句,這樣在隨後的執行中可以節省時間並增加程式碼的可讀性。

    然而,在Oracle環境中,開發人員實際上有更大的靈活性。當使用Statement或PreparedStatement物件時,Oracle資料庫會快取SQL語句以便以後使用。在一些情況下,由於驅動器自身需要額外的處理和在Java應用程式和Oracle伺服器間增加的網路活動,執行PreparedStatement物件實際上會花更長的時間。

    然而,除了緩衝的問題之外,至少還有一個更好的原因使我們在企業應用程式中更喜歡使用PreparedStatement物件,那就是安全性。傳遞給PreparedStatement物件的引數可以被強制進行型別轉換,使開發人員可以確保在插入或查詢資料時與底層的資料庫格式匹配。

    當處理公共Web站點上的使用者傳來的資料的時候,安全性的問題就變得極為重要。傳遞給PreparedStatement的字串引數會自動被驅動器忽略。最簡單的情況下,這就意味著當你的程式試著將字串“D'Angelo”插入到VARCHAR2中時,該語句將不會識別第一個“,”,從而導致悲慘的失敗。幾乎很少有必要建立你自己的字串忽略程式碼。

    在Web環境中,有惡意的使用者會利用那些設計不完善的、不能正確處理字串的應用程式。特別是在公共Web站點上,在沒有首先通過PreparedStatement物件處理的情況下,所有的使用者輸入都不應該傳遞給SQL語句。此外,在使用者有機會修改SQL語句的地方,如HTML的隱藏區域或一個查詢字串上,SQL語句都不應該被顯示出來。

     





    callablestatement 使用方法

    在 JDBC 中呼叫已儲存過程的語法如下所示。

    {call 過程名[(?, ?, ...)]}


      返回結果引數的過程的語法為:

    {? = call 過程名[(?, ?, ...)]}


      不帶引數的已儲存過程的語法類似:

    {call 過程名}


      通常,建立 CallableStatement 物件的人應當知道所用的 DBMS 是支援已儲存過程的,並且知道這些過程都是些什麼。然而,如果需要檢查,多種DatabaseMetaData 方法都可以提供這樣的資訊。例如,如果 DBMS 支援已儲存過程的呼叫,則supportsStoredProcedures 方法將返回 true,而getProcedures 方法將返回對已儲存過程的描述。CallableStatement 繼承 Statement 的方法(它們用於處理一般的 SQL 語句),還繼承了 PreparedStatement 的方法(它們用於處理 IN 參)。

      CallableStatement 中定義的所有方法都用於處理 OUT 引數或 INOUT 引數的輸出部分:註冊 OUT 引數的 JDBC 型別(一般 SQL 型別)、從這些引數中檢索結果,或者檢查所返回的值是否為 JDBC NULL。

      1、建立 CallableStatement 物件 

      CallableStatement 物件是用 Connection 方法 prepareCall 建立的。下例建立 CallableStatement 的例項,其中含有對已儲存過程 getTestData 呼叫。該過程有兩個變數,但不含結果引數:

    CallableStatement cstmt = con.prepareCall("{call getTestData(?, ?)}");


      其中?佔位符為IN、OUT還是INOUT引數,取決於已儲存過程getTestData。

      2、IN和OUT引數 

      將IN引數傳給 CallableStatement 物件是通過 setXXX 方法完成的。該方法繼承自 PreparedStatement。所傳入引數的型別決定了所用的setXXX方法(例如,用 setFloat 來傳入 float 值等)。

      如果已儲存過程返回 OUT 引數,則在執行 CallableStatement 物件以前必須先註冊每個 OUT 引數的 JDBC 型別(這是必需的,因為某些 DBMS 要求 JDBC 型別)。註冊 JDBC 型別是用 registerOutParameter 方法來完成的。語句執行完後,CallableStatement 的 getXXX 方法將取回引數值。正確的 getXXX 方法是為各引數所註冊的 JDBC 型別所對應的 Java 型別。換言之, registerOutParameter 使用的是 JDBC 型別(因此它與資料庫返回的 JDBC 型別匹配),而 getXXX 將之轉換為 Java 型別。

      作為示例,下述程式碼先註冊 OUT 引數,執行由 cstmt 所呼叫的已儲存過程,然後檢索在 OUT 引數中返回的值。方法 getByte 從第一個 OUT 引數中取出一個 Java 位元組,而 getBigDecimal 從第二個 OUT 引數中取出一個 BigDecimal 物件(小數點後面帶三位數):

     

    CallableStatement cstmt = con.prepareCall("{call getTestData(?, ?)}");
    cstmt.registerOutParameter(1, java.sql.Types.TINYINT);
    cstmt.registerOutParameter(2, java.sql.Types.DECIMAL, 3);
    cstmt.executeQuery();
    byte x = cstmt.getByte(1);
    java.math.BigDecimal n = cstmt.getBigDecimal(2, 3);

     

    3、INOUT引數 

      既支援輸入又接受輸出的引數(INOUT 引數)除了呼叫 registerOutParameter 方法外,還要求呼叫適當的 setXXX 方法(該方法是從 PreparedStatement 繼承來的)。setXXX 方法將引數值設定為輸入引數,而 registerOutParameter 方法將它的 JDBC 型別註冊為輸出引數。setXXX 方法提供一個 Java 值,而驅動程式先把這個值轉換為 JDBC 值,然後將它送到資料庫中。這種 IN 值的 JDBC 型別和提供給 registerOutParameter 方法的 JDBC 型別應該相同。然後,要檢索輸出值,就要用對應的 getXXX 方法。例如,Java 型別為byte 的引數應該使用方法 setByte 來賦輸入值。應該給registerOutParameter 提供型別為 TINYINT 的 JDBC 型別,同時應使用 getByte 來檢索輸出值。

      下例假設有一個已儲存過程 reviseTotal,其唯一引數是 INOUT 引數。方法setByte 把此引數設為 25,驅動程式將把它作為 JDBC TINYINT 型別送到資料庫中。接著,registerOutParameter 將該引數註冊為 JDBC TINYINT。執行完該已儲存過程後,將返回一個新的 JDBC TINYINT 值。方法 getByte 將把這個新值作為 Java byte 型別檢索。

    CallableStatement cstmt = con.prepareCall("{call reviseTotal(?)}");
    cstmt.setByte(1, 25);
    cstmt.registerOutParameter(1, java.sql.Types.TINYINT);
    cstmt.executeUpdate();
    byte x = cstmt.getByte(1);


    原文地址:http://lapulande.javaeye.com/blog/585424

                     http://www.cnblogs.com/raymond19840709/archive/2008/10/13/1309657.html

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/30066956/viewspace-1421016/,如需轉載,請註明出處,否則將追究法律責任。

相關文章