JDBC (三)

十、如何利用JDBC發送SQL語句?

  Statement對象用於將SQL語句發送到數據庫中。實際上有三種Statement對象,它們都作爲在給定連接上執行SQL語句的包容器:Statement、PreparedStatement(它從Statement繼承而來)和CallableStatement(它從PreparedStatement繼承而來)。它們都專用於發送特定類型的SQL語句:Statement對象用於執行不帶參數的簡單SQL語句;PreparedStatement對象用於執行帶或不帶IN參數的預編譯SQL語句;CallableStatement對象用於執行對數據庫已存儲過程的調用。

  Statement接口提供了執行語句和獲取結果的基本方法;PreparedStatement接口添加了處理IN參數的方法;而CallableStatement添加了處理OUT參數的方法。

  1. 創建Statement對象

  建立了到特定數據庫的連接之後,就可用該連接發送SQL語句。Statement對象用Connection的方法createStatement創建,如下列代碼段中所示:

      Connection con = DriverManager.getConnection(url,"sunny","");
      Statement stmt = con.createStatement();


  爲了執行Statement對象,被髮送到數據庫的SQL語句將被作爲參數提供給Statement的方法:

  ResultSet rs = stmt.executeQuery("SELECT a,b,c FROM Table2");

  2. 使用Statement對象執行語句

  Statement接口提供了三種執行SQL語句的方法:executeQuery、executeUpdate和execute。使用哪一個方法由SQL語句所產生的內容決定。

  方法executeQuery用於產生單個結果集的語句,例如SELECT語句。方法executeUpdate用於執行INSERT、UPDATE或DELETE語句以及SQL DDL(數據定義語言)語句,例如CREATE TABLE和DROP TABLE。INSERT、UPDATE或DELETE語句的效果是修改表中零行或多行中的一列或多列。executeUpdate的返回值是一個整數,指示受影響的行數(即更新計數)。對於CREATE TABLE或DROP TABLE等不操作行的語句,executeUpdate的返回值總爲零。

  執行語句的所有方法都將關閉所調用的Statement對象的當前打開結果集(如果存在)。這意味着在重新執行Statement對象之前,需要完成對當前ResultSet對象的處理。應注意,繼承了Statement接口中所有方法的PreparedStatement接口都有自己的executeQuery、executeUpdate和execute方法。Statement對象本身不包含SQL語句,因而必須給Statement.execute方法提供SQL語句作爲參數。PreparedStatement對象並不需要SQL語句作爲參數提供給這些方法,因爲它們已經包含預編譯SQL語句。

  CallableStatement對象繼承這些方法的PreparedStatement形式。對於這些方法的PreparedStatement或CallableStatement版本,使用查詢參數將拋出SQLException。

  3. 語句完成

  當連接處於自動提交模式時,其中所執行的語句在完成時將自動提交或還原。語句在已執行且所有結果返回時,即認爲已完成。對於返回一個結果集的executeQuery方法,在檢索完ResultSet對象的所有行時該語句完成。對於方法executeUpdate,當它執行時語句即完成。但在少數調用方法execute的情況中,在檢索所有結果集或它生成的更新計數之後語句才完成。

  有些DBMS將已存儲過程中的每條語句視爲獨立的語句;而另外一些則將整個過程視爲一個複合語句。在啓用自動提交時,這種差別就變得非常重要,因爲它影響什麼時候調用commit方法。在前一種情況中,每條語句單獨提交;在後一種情況中,所有語句同時提交。

  4. 關閉Statement對象

  Statement對象將由Java垃圾收集程序自動關閉。而作爲一種好的編程風格,應在不需要Statement對象時顯式地關閉它們。這將立即釋放DBMS資源,有助於避免潛在的內存問題。

  5. 使用方法execute

  execute方法應該僅在語句能返回多個ResultSet對象、多個更新計數或ResultSet對象與更新計數的組合時使用。當執行某個已存儲過程或動態執行未知SQL字符串(即應用程序程序員在編譯時未知)時,有可能出現多個結果的情況,儘管這種情況很少見。例如,用戶可能執行一個已存儲過程,並且該已存儲過程可執行更新,然後執行選擇,再進行更新,再進行選擇,等等。通常使用已存儲過程的人應知道它所返回的內容。

  因爲方法execute處理非常規情況,所以獲取其結果需要一些特殊處理並不足爲怪。例如,假定已知某個過程返回兩個結果集,則在使用方法execute執行該過程後,必須調用方法getResultSet獲得第一個結果集,然後調用適當的getXXX方法獲取其中的值。要獲得第二個結果集,需要先調用getMoreResults方法,然後再調用getResultSet方法。如果已知某個過程返回兩個更新計數,則首先調用方法getUpdateCount,然後調用getMoreResults,並再次調用getUpdateCount。

  對於不知道返回內容,則情況更爲複雜。如果結果是ResultSet對象,則方法execute返回true;如果結果是Javaint,則返回false。如果返回int,則意味着結果是更新計數或執行的語句是DL命令。在調用方法execute之後要做的第一件事情是調用getResultSet或getUpdateCount。調用方法getResultSet可以獲得兩個或多個ResultSet對象中第一個對象;或調用方法getUpdateCount可以獲得兩個或多個更新計數中第一個更新計數的內容。

  當SQL語句的結果不是結果集時,則方法getResultSet將返回null。這可能意味着結果是一個更新計數或沒有其它結果。在這種情況下,判斷null真正含義的唯一方法是調用方法getUpdateCount,它將返回一個整數。這個整數爲調用語句所影響的行數;如果爲-1則表示結果是結果集或沒有結果。如果方法getResultSet已返回null(表示結果不是ResultSet對象),則返回值-1表示沒有其它結果。也就是說,當下列條件爲真時表示沒有結果(或沒有其它結果):

  ((stmt.getResultSet()==null)&&(stmt.getUpdateCount()==-1))

  如果已經調用方法getResultSet並處理了它返回的ResultSet對象,則有必要調用方法getMoreResults以確定是否有其它結果集或更新計數。如果getMoreResults返回true,則需要再次調用getResultSet來檢索下一個結果集。如上所述,如果getResultSet返回null,則需要調用getUpdateCount來檢查null是表示結果爲更新計數還是表示沒有其它結果。

  當getMoreResults返回false時,它表示該SQL語句返回一個更新計數或沒有其它結果。因此需要調用方法getUpdateCount來檢查它是哪一種情況。在這種情況下,當下列條件爲真時表示沒有其它結果:

  ((stmt.getMoreResults()==false)&&(stmt.getUpdateCount()==-1))

  下面的代碼演示了一種方法用來確認已訪問調用方法execute所產生的全部結果集和更新計數:

 

      stmt.execute(queryStringWithUnknownResults);
      while(true){
             introwCount=stmt.getUpdateCount();
             if(rowCount>0){

                   //它是更新計數
                   System.out.println("Rows changed="+count);
                   stmt.getMoreResults();
                   continue;
             }
             if(rowCount==0){//DDL命令或0個更新
             System.out.println("No rows changed or statement was DDL command"); 
             stmt.getMoreResults();
             continue;
       }
       //執行到這裏,證明有一個結果集
       //或沒有其它結果
       ResultSet rs=stmt.getResultSet();
       if(rs!=null){
             ...//使用元數據獲得關於結果集列的信息
            while(rs.next()){
                 ...//處理結果
                stmt.getMoreResults();
                continue;
           }
           break;//沒有其它結果

      }


十一、如何獲得SQL語句的執行結果?

  ResultSet包含符合SQL語句中條件的所有行,並且它通過一套get方法(這些get方法可以訪問當前行中的不同列)提供了對這些行中數據的訪問。ResultSet.next方法用於移動到ResultSet中的下一行,使下一行成爲當前行。

  下面的代碼段是執行SQL語句的示例。該SQL語句將返回行集合,其中列1爲int,列2爲String,而列3則爲字節數組:

       

       Java.sql.Statementstmt=conn.createStatement(); 
       ResultSet r=stmt.executeQuery("SELECT a,b,c FROM Table1");
       while(r.next()){
              //打印當前行的值。
              Int i=r.getInt("a");
              String s=r.getString("b");
              Float f=r.getFloat("c");
              System.out.println("ROW="+i+" "+s+" "+f);
       }

 

  1. 行和光標

  ResultSet維護指向其當前數據行的光標。每調用一次next方法,光標向下移動一行。

  最初它位於第一行之前,因此第一次調用next將把光標置於第一行上,使它成爲當前行。隨着每次調用next導致光標向下移動一行,按照從上至下的次序獲取ResultSet行。

  在ResultSet對象或其父輩Statement對象關閉之前,光標一直保持有效。在SQL中,結果表的光標是有名字的。如果數據庫允許定位更新或定位刪除,則需要將光標的名字作爲參數提供給更新或刪除命令。可通過調用方法getCursorName獲得光標名。

  DatabaseMetaData.supportsPositionedDelete和supportsPositionedUpdate方法來檢查特定連接是否支持這些操作。當DBMS支持定位更新和刪除操作時,DBMS/驅動程序必須確保適當鎖定選定行,以使定位更新不會導致更新異常或其它併發問題。

  2. 列

  方法getXXX提供了獲取當前行中某列值的途徑。在每一行內,可按任何次序獲取列值。但爲了保證可移植性,應該從左至右獲取列值,並且一次性地讀取列值。

  列名或列號可用於標識要從中獲取數據的列。例如,如果ResultSet對象rs的第二列名爲"title",並將值存儲爲字符串,則下列任一代碼將獲取存儲在該列中的值:

  String s=rs.getString("title");
  String s=rs.getString(2);

  注意列是從左至右編號的,並且從列1開始。同時,用作getXXX方法的輸入的列名不區分大小寫。

  提供使用列名這個選項的目的是爲了讓在查詢中指定列名的用戶可使用相同的名字作爲getXXX方法的參數。另一方面,如果select語句未指定列名(例如在"select * from table1"中或列是導出的時),則應該使用列號。這些情況下,用戶將無法確切知道列名。

  有些情況下,SQL查詢返回的結果集中可能有多個列具有相同的名字。如果列名用作getXXX方法的參數,則getXXX將返回第一個匹配列名的值。因而,如果多個列具有相同的名字,則需要使用列索引來確保檢索了正確的列值。這時,使用列號效率要稍微高一些。

  關於ResultSet中列的信息,可通過調用方法ResultSet.getMetaData得到。返回的ResultSetMetaData對象將給出其ResultSet對象各列的編號、類型和屬性。

  如果列名已知,但不知其索引,則可用方法findColumn得到其列號。

  3. 數據類型和轉換

  對於getXXX方法,JDBC驅動程序試圖將基本數據轉換成指定Java類型,

  然後返回適合的Java值。例如,如果getXXX方法爲getString,而基本數據庫中數據類型爲VARCHAR,則JDBC驅動程序將把VARCHAR轉換成JavaString。getString的返回值將爲JavaString對象。

  4. 對非常大的行值使用流
  ResultSet可以獲取任意大的LONGVARBINARY或LONGVARCHAR數據。方法getBytes和getString將數據返回爲大的塊(最大爲Statement.getMaxFieldSize的返回值)。但是,以較小的固定塊獲取非常大的數據可能會更方便,而這可通過讓ResultSet類返回Java.io.Input流來完成。從該流中可分塊讀取數據。注意:必須立即訪問這些流,因爲在下一次對ResultSet調用getXXX時它們將自動關閉(這是由於基本實現對大塊數據訪問有限制)。

  JDBCAPI具有三個獲取流的方法,分別具有不同的返回值:

  ·getBinaryStream:返回只提供數據庫原字節而不進行任何轉換的流。

  ·getAsciiStream返回提供單字節ASCII字符的流。

  ·getUnicodeStream返回提供雙字節Unicode字符的流。

  注意:它不同於Java流,後者返回無類型字節並可(例如)通用於ASCII和Unicode字符。下列代碼演示了       getAsciiStream的用法:

 

         Java.sql.Statementstmt=con.createStatement();
         ResultSet r=stmt.executeQuery("SELECT x FROM Table2");
         //現在以4K塊大小獲取列1結果:
         byte buff=newbyte[4096];
         while(r.next()){
                Java.io.InputStream fin=r.getAsciiStream(1);
                for(;;){
                      intsize=fin.read(buff);
                      if(size==-1){//到達流末尾
                            break;
                     }
                    //將新填充的緩衝區發送到ASCII輸出流:
                    output.write(buff,0,size);
               }
         }

 

  5. NULL結果值

  要確定給定結果值是否是JDBC NULL,必須先讀取該列,然後使用ResultSet.wasNull
方法檢查該次讀取是否返回JDBC NULL。

  當使用ResultSet.getXXX方法讀取JDBC NULL時,方法wasNull將返回下列值之一:

  (1)Javanull值

  對於返回Java對象的getXXX方法(例如getString、getBigDecimal、getBytes、getDate、getTime、getTimestamp、getAsciiStream、getUnicodeStream、getBinaryStream、getObject等)。

  (2)零值:對於getByte、getShort、getInt、getLong、getFloat和getDouble。

  (3)false值:對於getBoolean。

  6. 可選結果集或多結果集

  通常使用executeQuery(它返回單個ResultSet)或executeUpdate(它可用於任何數據庫修改語句,並返回更新行數)可執行SQL語句。但有些情況下,應用程序在執行語句之前不知道該語句是否返回結果集。此外,有些已存儲過程可能返回幾個不同的結果集和/或更新計數。

  爲了適應這些情況,JDBC提供了一種機制,允許應用程序執行語句,然後處理由結果集和更新計數組成的任意集合。這種機制的原理是首先調用一個完全通用的execute方法,然後調用另外三個方法,getResultSet、getUpdateCount和getMoreResults。這些方法允許應用程序一次一個地研究語句結果,並確定給定結果是ResultSet還是更新計數。

  用戶不必關閉ResultSet;當產生它的Statement關閉、重新執行或用於從多結果序列中獲取下一個結果時,該ResultSet將被Statement自動關閉。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章