JDBC Statement PreparedStatement CallableStatement

        在 JDBC 應用程序中,JDBC 語句對象用於將 SQL 語句發送到數據庫服務器。一個語句對象與一個鏈接相關聯,應用程序與數據庫服務器之間的通訊由語句對象來處理。java


JDBC 中有三種類型的語句對象:sql

    1. 常規語句(General statement)數據庫

    2. 預置語句(Prepared statement)緩存

    3. 可調用語句(Callable statement)服務器

語句對象與一個鏈接相關聯,因此要建立一個語句對象,首先應該創建一個數據庫鏈接。鏈接的獲取見上一篇博客。oop


關於這三種語句,我的以爲最有價值的就是 預置語句,下面說說爲何性能

1)預置語句 能夠避免sql參數注入。若是使用常規的statement,那麼用戶能夠注入sql語句,把一個本應該是傳入參數的變量變成了SQL注入變量。然而,若是使用預置語句,那麼變量的值是不可能傳入SQL的。這個在查詢的時候若是你把條件設爲 or 1=1 獲得體現。優化

2)預置語句 能夠高效操做,緣由是語句只要編譯一次。對於一條模板SQL,咱們可能須要執行屢次,每次只是傳入的參數不同,那麼若是咱們使用常規語句則會每次執行時在數據庫編譯一次,然而,若是採用預置語句,則只會編譯一次,這樣效率大大提升。this

3)預置語句 能夠批量操做。對於一些特殊的業務邏輯處理,咱們可能會一次更新多條數據,然而若是咱們每次都去獲取語句執行SQL的話效率必然下降,可是,若是採用預置語句的批量操做,效率會提升不少。url

4)關於可更新的結果集以及可滾動的結果集,在下一篇博客中結果集說明,注意批量操做與結果集的滾動與更新並非預置語句獨有的優勢,常規語句也能夠實現,預置語句兩個特色就是預編譯和防止SQL語句做爲變量注入。


下面是對三種語句的詳細介紹,內容轉載自

 http://blog.csdn.net/athenaer/article/details/8696311


常規語句

可使用鏈接的 createStatement 方法建立這種語句。這種語句專用於不須要傳遞任何值做爲參數的 SQL 語句。

清單 2. 演示建立語句的示例代碼

                Statement stmt = con.createStatement();
      cmd = "create database testDB;";
      rc = stmt.executeUpdate(cmd);
stmt.close();


預置語句

預置語句是 statement 類的一個子類。預置語句與 statement 類的主要區別在於,前者能夠只編譯和優化一次,而後經過設置不一樣的參數值屢次使用。因此,若是想屢次執行一條語句,那麼預置語句是更好的選擇。因爲已經預先編譯好,因此減小了執行時間。所以,預置語句的優勢是,它不只包含一條 SQL 語句,並且仍是一條預先編譯好的 SQL 語句。另外一個區別是,SQL 語句在建立後就被提供給預置語句。

清單 3. 解釋預置語句的示例代碼

                PreparedStatement pstmt = con.prepareStatement("UPDATE tab1 "+
                    "set col1 = ? where key = 1");
pstmt.setShort(1, (short)2);
int rowcount = pstmt.executeUpdate();

在此,同一個預置語句可用於不一樣的 col1 值。參數一旦設定,它的值將保持不變,直到被從新設置或者 clearParameters 被調用。這項特性使得預置語句能夠用於批量處理 INSERT/UPDATE

批量更新

經過設置多個值,批量更新特性提升了須要屢次執行的語句的性能。這樣能夠將多個更新操做提交到一個數據源並進行一次性處理。語句對象也可使用批量更新。但語句對象提交不一樣的 SQL 語句進行批處理,而預置語句提交的是一組參數。

清單 4 顯示瞭如何使用預置語句進行批量插入:

清單 4. 演示批量更新的示例代碼

                PreparedStatement pst = conn.prepareStatement("insert into tab1 values (?)");
for loop....
{
	pst.setInt (1, i);
	pst.addBatch();
}
pst.executeBatch();



addBatch 方法將語句添加到一個緩存中,而後使用 executeBatch() 方法轉儲到數據庫中。因此它節省了語句的編譯/優化,由於它只編譯一次(對於預置語句),並且還節省了與服務器之間的往返,由於它一次性發送了批量插入。

可調用語句

這是調用 SQL 語句的第三種方法,它提供了一種從 Java™ 程序中調用服務器上的存儲過程的方式。可調用語句也須要先做準備,而後使用 set 方法設置它們的參數。能夠經過如下兩種方式設置參數值:

  1. 順序位置

  2. 命名參數

順序位置是傳統的參數設置方式,它根據參數在 CallableStatements 中的位置來設置參數。可是,命名參數則提供了更大的靈活性,它容許根據名稱而不是順序位置來設置參數。在調用例程時,必須以名稱或順序格式指定 CallableStatement 的參數。例如,若是對一個參數使用了參數名稱,那麼對全部其餘參數也必須使用參數名稱。

在調用具備許多參數,並且其中一些參數有默認值的存儲過程時,命名參數特別有用。若是過程是唯一的,那麼能夠省略有默認值的參數,而且能夠按任意順序輸入參數。命名參數使應用程序更加健壯,因此,即便存儲過程當中參數的順序發生了改變,也沒必要修改應用程序。

JDBC 驅動程序提供了 DatabaseMetaData.supportsNamedParameters() 方法來確認驅動程序和 RDMS 是否支持 CallableStatement 中的命名參數。若是支持命名參數,則系統返回 true。例如:

清單 5. supportsNamedParameters() 的使用

                Connection myConn = . . .   // connection to the RDBMS for Database
      DatabaseMetaData dbmd = myConn.getMetaData();
      if (dbmd.supportsNamedParameters() == true)
      {
          System.out.println("NAMED PARAMETERS FOR CALLABLE"
                            + "STATEMENTS IS SUPPORTED");
      }

獲取存儲過程的參數名稱

可使用 DatabaseMetaData 的 getprocedureColumns 獲取存儲過程的參數名稱,該方法的定義如清單 6 所示:

清單 6. getProcedureColumn() 方法的使用

                Connection myConn = . . .   // connection to the RDBMS for Database
 . .
      DatabaseMetaData dbmd = myConn.getMetaData();
      ResultSet rs = dbmd.getProcedureColumns(
       "myDB", schemaPattern, procedureNamePattern, columnNamePattern);
      rs.next() {
          String parameterName = rs.getString(4);
 - - - or - - -
 String parameterName = rs.getString("COLUMN_NAME"); 
 - - -
          System.out.println("Column Name: " + parameterName);

與 getProcedureColumns() 方法的參數相匹配的全部列的名稱都將被顯示。

清單 7 顯示了 CallableStatements 中的命名參數的使用。

建立存儲過程

清單 7. 可調用 OUT 參數的使用

                create procedure createProductDef(productname   varchar(64),
                 productdesc  varchar(64),
                 listprice    float,
                 minprice     float,
             out prod_id      float);
. . .
  let prod_id="value for prod_id";
end procedure;

清單 8 中的 Java 代碼首先建立一個有 5 個參數的 CallableStatement,這 5 個參數與存儲過程當中的參數相對應。JDBC 調用的括號中的問號字符 (?) 對參數進行引用。設置或註冊全部的參數。使用格式 cstmt.setString("arg", name); 命名參數,其中 arg 是相應的存儲過程當中的參數名稱。這裏不須要按照存儲過程當中的參數順序來命名參數。


清單 8. 可調用命名參數的使用

                String sqlCall = "{call CreateProductDef(?,?,?,?,?)}";
      CallableStatement cstmt = conn.prepareCall(sqlCall);

      cstmt.setString("productname", name);     // Set Product Name.
      cstmt.setString("productdesc", desc);     // Set Product Description.
      cstmt.setFloat("listprice", listprice);   // Set Product ListPrice.
      cstmt.setFloat("minprice", minprice);     // Set Product MinPrice.

      // Register out parameter which should return the product is created.

      cstmt.registerOutParameter("prod_id", Types.FLOAT);

      // Execute the call.
      cstmt.execute();

      // Get the value of the id from the OUT parameter: prod_id
      float id = cstmt.getFloat("prod_id");


若是 CallableStatement 中的參數數量少於存儲過程當中的參數數量,那麼剩下的參數必須有默認值。不須要爲有默認值的參數設置值,由於服務器會自動使用默認值。例如,若是一個存儲過程有 10 個參數,其中 4 個參數沒有默認值,6 個參數有默認值,那麼在 CallableStatement 中必須至少有 4 個問號。也可使用 5 個、6 個或至多 10 個問號。在下面這個唯一的存儲過程當中,參數listprice 和 minprice 有默認值:

清單 9. 建立包括具備默認值的參數的過程

                create procedure createProductDef(productname   varchar(64),
                 productdesc  varchar(64),
                 listprice    float default 100.00,
                 minprice     float default  90.00,
             out prod_id      float);
. . .
  let prod_id = value for prod_id;
end procedure;

清單 10 中的 Java 代碼使用少於存儲過程當中參數數量的參數(存儲過程當中有 5 個參數,而代碼中只使用 4 個參數)調用存儲過程。因爲 listprice 有一個默認值,所以能夠在 CallableStatement 中省略它。

清單 10. 默認參數的使用

                String sqlCall = "{call CreateProductDef(?,?,?,?)}";
                                              // 4 params for 5 args
      CallableStatement cstmt = conn.prepareCall(sqlCall);

      cstmt.setString("productname", name);   // Set Product Name.
      cstmt.setString("productdesc", desc);   // Set Product Description.
    
      cstmt.setFloat("minprice", minprice);   // Set Product MinPrice.

      // Register out parameter which should return the product id created.

      cstmt.registerOutParameter("prod_id", Types.FLOAT);

      // Execute the call.
      cstmt.execute();

      // Get the value of the id from the OUT parameter: prod_id
      float id = cstmt.getFloat("prod_id");

若是可調用語句包含 OUT 或 INOUT 參數,那麼須要使用 CallableStatement 的 registerOutParameter 註冊這些參數。清單 11 使用out 參數 prod_id 建立一個具備 OUT 參數的存儲過程。相似地,可使用關鍵字 INOUT 建立 INOUT 參數。

清單 11. INOUT 和 OUT 參數的使用

                create procedure createProductDef(productname   varchar(64),
                 productdesc  varchar(64),
             inout    listprice    float default 100.00,
                 minprice     float default  90.00,
             out prod_id      float);

清單 12 使用 CallableStatements registerOutparameter 方法註冊 CallableStatement 的 out 參數。

清單 12. 使用 CallableStatement 註冊 OUT 參數

                cstmt.registerOutParameter("prod_id", Types.FLOAT);

清單 13 將使用命名參數特性的全部語句合併在一塊兒:

清單 13. 演示命名參數功能的程序

                package Callable;

import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.Types;
public class out1 {
    static Connection conn;	
    public static void main(String[] args) {
        getConnect();
        System.out.println("Connection Established");
        createProc();
        runthis();
        System.out.println("\n=============Finished=============");
        System.exit(0);
    }
    private static void getConnect() {
        try
        {
            Class.forName("com.informix.jdbc.IfxDriver");
            String url = "jdbc:informix-sqli://host name or ip :porn number/database
                 name:informixserver=dbservername;";
            System.out.println("URL: "+url);
            conn = DriverManager.getConnection(url);
            }
        catch( Exception e )
        {
            e.printStackTrace();
            System.exit(1);
        }
    }
    private static void createProc() {
        String str=null;
        Statement stmt = null;
        try 
        {
            stmt = conn.createStatement();
        } 
        catch (SQLException e2) 
        {
            e2.printStackTrace();
        }
        str="drop function c_out_proc";
        try 
        {
            stmt.executeUpdate (str);
        } 
        catch (SQLException e1) 
        {	}
        str = "create function  c_out_proc ( i int, OUT d varchar(20) ) \n" +
        "returning float; \n" +
        "define f float; \n" +
        "let d= \"Hello OUT\"; \n" +
        "let f=i*2; \n" +
        "return f; \n" + 
        "end function; \n";
        try
        {
            stmt.executeUpdate (str);
            System.out.println("Function created \n");
        }
        catch (SQLException e)
        {
            System.out.println("Error on creating function: " + e.toString());
            System.exit(1);
        }		
    }
    private static void runthis() 
    {
        CallableStatement cstmt = null;
        String command = "{? = call c_out_proc(?, ?)}  ";
        try 
        {
            cstmt = conn.prepareCall (command);
            cstmt.setInt(1, 2);
            cstmt.registerOutParameter(2, Types.VARCHAR);
            ResultSet rs = cstmt.executeQuery();
            if (rs == null)
            {
                System.out.println("rs is null *** this is BAD.");
                System.exit(0);
            }
            else
            {
                rs.next();
                System.out.println(rs.getFloat(1));
                System.out.println(cstmt.getString(2));
            }
        } 
        catch (SQLException e) 
        {
            e.printStackTrace();
        }
    }
}
相關文章
相關標籤/搜索