<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    Vincent

    Vicent's blog
    隨筆 - 74, 文章 - 0, 評論 - 5, 引用 - 0
    數據加載中……

    JDBC 查詢日志變得簡單

    JDBC java.sql.PreparedStatement接口的簡單擴展可以使查詢記錄更少犯錯,同時整理您的代碼。在本文中,IBM電子商務顧問Jens Wyke向您介紹如何應用基本的封裝技術(“通過封裝來實現擴展”也稱為Decorator設計模式)來獲得最滿意的結果。

    在大多數情況下,JDBC PreparedStatements 使執行數據庫查詢更簡便并可以顯著提升您整體應用程序的性能。當談到日志查詢語句時 PreparedStatement 接口就顯得有些不足了。 PreparedStatement 的優勢在于其可變性,但是一個好的日志條目必須正確描述如何將SQL發送到數據庫,它將密切關注用實際的參數值來替換所有參數占位符。雖然有多種方法可以解決這一難題,但沒有任何一種易于大規模實施并且大部分將擾亂您的程序代碼。

    在本文中,您將了解到如何擴展JDBC PreparedStatement 接口來進行查詢日志。 LoggableStatement 類實現 PreparedStatement 接口,但添加用于獲得查詢字符串的方法,使用一種適用于記錄的格式。使用 LoggableStatement 類可以減少日志代碼中發生錯誤的幾率,生成簡單且易于管理的代碼。

    注意:本文假設您有豐富的JDBC和 PreparedStatement 類經驗。

    典型日志解決方案

    表1介紹了數據庫查詢時通常是如何使用 PreparedStatement (雖然忽略了初始化和錯誤處理)。在本文中,我們將使用SQL query SELECT 做為例子,但討論使用其它類型的SQL語句,如 DELETEUPDATEINSERT 。


    表1:一個典型的SQL數據庫查詢
    												
    														String sql = "select foo, bar from foobar where foo < ? and bar = ?";
        String fooValue = new Long(99);
        String barValue = "christmas";
    
        Connection conn = dataSource.getConnection();
        PreparedStatement pstmt = conn.prepareStatement(sql);
    
        pstmt.setLong(1,fooValue);
        pstmt.setString(2,barValue);
    
        ResultSet rs = pstmt.executeQuery();
    
        // parse result...
    
    
    												
    										

    表1中一個好的查詢日志條目看起來應與下面有幾分類似:

    												
    														Executing query: select foo,bar from foobar where foo < 99 and 
    bar='christmas'
    
    												
    										

    下面是查詢的日志代碼的一個例子。注意:表1中的問號已經被每個參數的值替換。

    												
    														System.out.println("Executing query: select foo, bar from foobar where foo
    < "+fooValue+" and bar = '+barValue+"'")
    
    												
    										

    一種更好的方法是創建方法,我們稱之為 replaceFirstQuestionMark ,它讀取查詢字符串并用參數值替換問號,如表2所示。這類方法的使用無需創建復制的字符串來描述SQL語句。


    表 2:使用replaceFirstQuestionMark來進行字符串替換
    												
    														      // listing 1 goes here
    
         sql = replaceFirstQuestionMark(sql, fooValue);
         sql = replaceFirstQuestionMark(sql, barValue);
         System.out.println("Executing query: "+sql);
    
    												
    										

    雖然這些解決方案都易于實施,但沒有一種是完美的。問題是在更改SQL模板的同時也必須更改日志代碼。您將在某一點上犯錯幾乎是不可避免的。查詢將更改但您忘記了更新日志代碼,您將結束與將發送到數據庫的查詢不匹配的日志條目 -- 調試惡夢。

    我們真正需要的是一種使我們能夠一次性使用每個參數變量(在我們的實例中為 fooValuebarValue )的設計方案。我們希望有一種方法,它使我們能夠獲得查詢字符串,并用實際的參數值替換參數占位符。由于 java.sql.PreparedStatement 沒有此類方法,我們必須自己實現。





    回頁首


    定制解決方案

    我們的 PreparedStatement 定制實施將做為圍繞JDBC驅動器提供的“真實語句(real statement)”的封裝器(Wrapper)。封裝器語句將轉發所有方法調用(例如 setLong(int, long)setString(int,String) ) 到“真實語句”。在這樣做之前它將保存相關的參數值,從而它們可以用于生成日志輸出結果。

    表3介紹了 LoggableStatement 類如何實現 java.sql.PreparedStatement ,以及它如何使用JDBC連接和SQL模板作為輸入來構建。


    表3:LoggableStatement實現java.sql.PreparedStatement
    												
    														  public class LoggableStatement implements java.sql.PreparedStatement {
    
         // used for storing parameter values needed
          // for producing log
         private ArrayList parameterValues;     
              
         // the query string with question marks as  
         // parameter placeholders
         private String sqlTemplate;       
                   
         // a statement created from a real database     
         // connection                                       
         private PreparedStatement wrappedStatement; 
                                                     
    
        public LoggableStatement(Connection connection, String sql) 
          throws SQLException {
          // use connection to make a prepared statement
          wrappedStatement = connection.prepareStatement(sql);
          sqlTemplate = sql;
          parameterValues = new ArrayList();
        }
         }
    
    												
    										





    回頁首


    LoggableStatement如何工作

    表4介紹了 LoggableStatement 如何向 saveQueryParamValue() 方法添加一個調用,以及在方法 setLongsetString 的“真實語句”上調用相應的方法。我們采用與用于參數設置的所有方法(例如 setChar 、 setLongsetRefsetObj )相同的方式來增加 saveQueryParamValue() 調用。表4還顯示了在不調用 saveQueryParamValue() 的情況下如何封裝方法 executeQuery ,因為它不是一個“參數設置”方法。


    表4:LoggableStatement 方法
    												
    														     public void setLong(int parameterIndex, long x) 
             throws java.sql.SQLException {
          wrappedStatement.setLong(parameterIndex, x);
          saveQueryParamValue(parameterIndex, new Long(x));
       }
    
       public void setString(int parameterIndex, String x) 
           throws java.sql.SQLException {
          wrappedStatement.setString(parameterIndex, x);
          saveQueryParamValue(parameterIndex, x);
       }
    
      public ResultSet executeQuery() throws java.sql.SQLException {
         return wrappedStatement.executeQuery();
       }
    
    												
    										

    表5中顯示了 saveQueryParamValue() 方法。它把每個參數值轉換成 String 表示,保存以便 getQueryString 方法日后使用。缺省情況下,一個對象使用其 toString 方法將被轉換成 String ,但如果對象是 StringDate ,它將用單引號('')表示。 getQueryString() 方法使您能夠從日志復制大多數查詢并進行粘貼,無需修改交互式SQL處理器就可進行測試和調試。您可以根據需要修訂該方法來轉換其它類的參數值。


    表5:saveQueryParamValue()方法
    												
    														  private void saveQueryParamValue(int position, Object obj) {
          String strValue;
          if (obj instanceof String || obj instanceof Date) {
               // if we have a String, include '' in the saved value
               strValue = "'" + obj + "'";
          } else {
               if (obj == null) {
                    // convert null to the string null
                     strValue = "null";
               } else {
                    // unknown object (includes all Numbers), just call toString
                    strValue = obj.toString();
               }
          }
          // if we are setting a position larger than current size of 
          // parameterValues, first make it larger
          while (position >= parameterValues.size()) {
               parameterValues.add(null);
          }
          // save the parameter
          parameterValues.set(position, strValue);
     }
    
    												
    										

    當我們使用標準方法來設置所有參數時,我們在 LoggableStatement 中簡單調用 getQueryString() 方法來獲得查詢字符串。所有問號都將被真正的參數值替換,它準備輸出到我們選定的日志目的地。





    回頁首


    使用LoggableStatement

    表6顯示如何更改表1和表2中的代碼來使用 LoggableStatement 。將 LoggableStatement 引入到我們的應用程序代碼中可以解決復制的參數變量問題。如果改變了SQL模板,我們只需更新 PreparedStatement 上的參數設置調用(例如添加一個 pstmt.setString(3,"new-param-value") )。這一更改將在日志輸出結果中反映出,無需任何記錄代碼的手工更新。


    表6:使用LoggableStatement&#160
    												
    														    String sql = "select foo, bar from foobar where foo < ? and bar = ?";
        long fooValue = 99;
        String barValue = "christmas";
    
        Connection conn = dataSource.getConnection();
        PreparedStatement pstmt;
    
        if(logEnabled) // use a switch to toggle logging.
            pstmt = new LoggableStatement(conn,sql);
        else
            pstmt = conn.prepareStatement(sql);
    
        pstmt.setLong(1,fooValue);
        pstmt.setString(2,barValue);
    
        if(logEnabled)
           System.out.println("Executing query: "+
             ((LoggableStatement)pstmt).getQueryString());
    
        ResultSet rs = pstmt.executeQuery();
    
    												
    										





    回頁首


    結束語

    使用本文介紹的非常簡單的步驟,您可以為查詢記錄擴展JDBC PreparedStatement 接口。我們在此處使用的技術可以被視為“通過封裝來實現擴展”,或作為Decorator設計模式的一個實例(見 參考資料)。通過封裝來實現擴展在當您必須擴展API但subclassing不是一項可選功能時極其有用。

    posted on 2006-08-24 17:50 Binary 閱讀(247) 評論(0)  編輯  收藏 所屬分類: j2se

    主站蜘蛛池模板: 国内免费高清在线观看| 野花视频在线官网免费1| 亚洲AV无码成人精品区天堂| 国产偷v国产偷v亚洲高清| 亚洲日本中文字幕区| 国产成人精品日本亚洲直接| 亚洲AV日韩AV永久无码色欲| 国产免费伦精品一区二区三区| 99久久精品国产免费| 日本19禁啪啪无遮挡免费动图| 亚洲 自拍 另类小说综合图区| 久久国产亚洲电影天堂| 免费人人潮人人爽一区二区| 亚洲一级免费毛片| 亚洲国产精品毛片av不卡在线| 亚洲第一二三四区| 成人免费夜片在线观看| av大片在线无码免费| 亚洲日韩精品一区二区三区无码 | 免费无码VA一区二区三区| 亚洲AV日韩AV天堂久久| 国产又大又粗又长免费视频| 亚洲精品无码成人片久久不卡| 两个人看的www高清免费视频 | 曰韩亚洲av人人夜夜澡人人爽| 亚洲欧洲另类春色校园小说| 成在人线av无码免费高潮水| 成人国产mv免费视频| yy一级毛片免费视频| 免费大香伊蕉在人线国产| 国产亚洲日韩一区二区三区| 免费污视频在线观看| jjzz亚洲亚洲女人| 无码午夜成人1000部免费视频| 亚洲va国产va天堂va久久| 中字幕视频在线永久在线观看免费 | 免费无码国产V片在线观看| 亚洲国产精彩中文乱码AV| 久久国产精品免费一区二区三区| 亚洲精品视频在线| 国产精品久久久久影院免费|