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

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

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

    posts - 42,comments - 83,trackbacks - 0

            前幾天有客戶問我這么個問題,他們在weblogic中配置了prepared statement cache, 而他們應用中有操作DDL的地方,比如alter table什么的,這時候如果使用cached prepared statement的話,Oracle端會拋出SQLException: 違反協議。其實這個問題,weblogic 文檔中已經有描述,如下:
            http://e-docs.bea.com/wls/docs81/ConsoleHelp/jdbc_connection_pools.html#1107805

            大概意思是:這個依賴數據庫,需要看數據庫端怎么處理這樣的prepared statement. 最初我認為只要在weblogic 端手工清理掉整個cache就可以了(weblogic在prepared statement 出現異常的時候,會主動將wrapper connection上對應的prepared statement cache清掉,下次調用的時候會重建prepared statement,所以手工清理cache是完全多余的),但實際結果并不如想象的那樣。即使我們clear掉prepared statement cache, 重新創建一個prepared statement的話,問題同樣得不到解決。 為什么? 怎么辦?作了幾個相關的測試后, 結論是:這個行為依賴于DB的physical connection, 而不是單個的prepared statement,出現這樣的問題后,能做的有如下2種方式:
             1:客戶端處理prepared statement拋出的異常, catch到異常后,需要將physical connection拿出來close掉。之所以建議這樣,客戶從data source中拿出的是個logical connection,而physical connection一直在connection pool。如果簡單的close掉logical connection, 重新去拿一個logical connection的話,weblogic無法保證返回的connection用了不同的physical connection。后面會有詳細的解決辦法。
             2:等待,大約一分鐘左右,可以正常操作。

            首先看看為什么?
             好了,我們可以用用下面的代碼測試一下:在測試程序run起來以后, 通過sql plus去改變后端test table的結構,比如alter table test  add(key1 varchar(10))

     1 package test.jdbc;
     2 
     3 import oracle.jdbc.OracleDriver;
     4 import java.sql.DriverManager;
     5 import java.sql.Connection;
     6 import java.sql.PreparedStatement;
     7 import java.sql.ResultSet;
     8 
     9 public class OracleDriverTest {
    10     
    11     public static void main(String args[])
    12     {
    13         try
    14         {
    15             OracleDriver driver = (OracleDriver)Class.
    16                 forName("oracle.jdbc.OracleDriver").newInstance();
    17             DriverManager.registerDriver(driver);
    18             String url="jdbc:oracle:thin:@localhost:1521:coffeedb";
    19             Connection conn = DriverManager.getConnection(url, "system""coffee");
    20             PreparedStatement pstmt = conn.prepareStatement("select * from Test");
    21             for(int loop=0; loop<10; loop++)
    22             {
    23                 try
    24                 {
    25                     System.out.println(pstmt.toString());
    26                     ResultSet rs = pstmt.executeQuery();
    27                     while(rs.next())
    28                     {
    29                         String val = rs.getString(1);
    30                         System.out.println(val);
    31                     }
    32                     rs.close();
    33                     Thread.currentThread().sleep(5000);
    34                 }catch(java.sql.SQLException se)
    35                 {
    36                     //Thread.currentThread().sleep(10000);
    37                     se.printStackTrace();
    38                     System.out.println("get exception, remake prepared statement in loop: " + loop);
    39                     /*
    40                      * if we just remake a prepared statement, SQLException will be thrown still, to
    41                      * slove such issue, we have to remake a physical connection. To do the test, we
    42                      * can comment the next line at first to see what will happen and then we activate
    43                      * it, to see what will happen this time. 
    44                      */ 
    45                     //conn = DriverManager.getConnection(url, "system", "coffee");
    46                     pstmt = conn.prepareStatement("select * from Test");
    47                     continue;
    48                 }
    49             }
    50             pstmt.close();
    51             conn.close();
    52             
    53         }catch(Exception e)
    54         {
    55             try
    56             {
    57                 //Thread.currentThread().sleep(10000);
    58                 System.out.println("catch exception in main()");
    59                 e.printStackTrace();
    60             }catch(Exception e1)
    61             {
    62                 e1.printStackTrace();
    63             }
    64         }    
    65     }
    66 }
    67 


           如代碼中的注釋說的一樣,單純的重建prepared statement是沒用的,需要重建physical connection. 這個代碼中connection沒有通過weblogic, 直接從driver manager拿connection, 問題一樣能夠復現, 跟weblogic沒關系了吧。

             好了,知道為什么了,但怎么辦呢? physical connection是weblogic在connection pool中維護的,我們怎么去控制它們啊?看文檔, weblogic的jdbc programming提到了具體的操作方法,鏈接如下:
            http://e-docs.bea.com/wls/docs81/jdbc/thirdparty.html#1108224

    1      java.sql.Connection vendorConn =        ((WLConnection)conn).getVendorConnection();      
    2 // do not close vendorConn     
    3 // You could also cast the vendorConn object //to a vendor interface, such as:      
    4 // oracle.jdbc.OracleConnection vendorConn = (OracleConnection) 

            
            文檔中不建議我們自己去關閉vendor connection,而是由connection pool自己去管理,connection pool通過Remove Infected Connections Enabled來控制physical connection如何還池,

    Applies only to physical database connections.

    When set to true, the physical connection is not returned to the connection pool after the application closes the logical connection. Instead, the physical connection is closed and recreated.

    Note: It is recommended that you set this flag to true as such connections are not managed by WebLogic Server.

    When set to false, if you close the logical connection, the physical connection is returned to the connection pool. If you use this setting, ensure that the database connection is suitable for reuse by other applications.

    This parameter is applicable only if the application gets a connection from the connection pool and then calls the getVendorConnection() method on that object. The getVendorConnection() method returns a vendor specific connection to the caller of the method which might leave the connection pool in an inconsistent state. As a result, WebLogic Server might remove it from the pool assuming it is an infected connection.

    Enabling this attribute will have an impact on performance as it will essentially disable the pooling of connections. This is because connections will be removed from the pool and replaced with new connections.


            因為我們這個問題必須關閉physical connection, 所以采用默認配置就可以了。你也許還會有疑問,physical connection被關閉了,如果我反復搞這么幾次,connection不就被關完了?其他應用怎么辦?不用擔心,有connection pool呢,在getVendorConnection()被調用的時候, connection會檢查Remove Infected Connections Enabled,如果為true,即這個logical connection對應的physical connection不會被重用,它會schedule一個創建physical connection的動作,以補充那個拋棄我們的phisical connection。最后關閉連接的時候,logical connection會被廢棄,physical connection被關閉。

            而我在測試中,嘗試自己去關閉vendor connection,如下:

     1     private void initializeConnection() throws SQLException
     2     {
     3         //this test should be run in local jvm, as oracle.jdbc.driver.T4CConnection
     4         //is not a serializable object.
     5         //java.io.NotSerializableException: oracle.jdbc.driver.T4CConnection
     6         this.conn = this.retriver.getJBDCConnection(dsName);
     7         this.wlConn = (WLConnection)this.conn;
     8         this.oraConn = (OracleConnection)this.wlConn.getVendorConnection();
     9         System.out.println(this.conn.toString());
    10         System.out.println(this.oraConn.toString());
    11     }
    12     
    13     private void pstmtTest()
    14     {
    15         try
    16         {
    17             PreparedStatement pstmt = this.conn.prepareStatement("select * from Test");
    18             System.out.println(pstmt.toString());
    19             ResultSet rs = pstmt.executeQuery();
    20             while(rs.next())
    21             {
    22                 String val = rs.getString(1);
    23                 System.out.println(val);
    24             }
    25             rs.close();
    26             pstmt.close();
    27             this.oraConn.close();
    28             this.conn.close();
    29         }catch(Exception e)
    30         {
    31             try
    32             {
    33                 this.oraConn.close();
    34                 this.conn.close();
    35             }catch(Exception e1)
    36             {
    37                 e1.printStackTrace();
    38             }
    39             e.printStackTrace();
    40         }
    41     }


            測試也沒什么問題,應該是oracle connection在關閉connection時先去檢查connection 狀態,如果已經關閉,則忽略這個動作,否則weblogic在關閉physical connection的時候應該收到SQLException。雖然這么做沒什么問題,但還是建議大家按照文檔上的說明,不要主動關閉phisical connection, 而讓connection pool自己去管理。

            總結一下:要處理這個問題,在catch到preparedStatement.execute***()拋出的SQLException時候,將從data source中get出來的connection cast成WLConnection, 然后調用getVendorConnection()即可,不要做其他任何處理。但有一個限制,這個方法必須在server端執行, 因為Vendor Connection不是個serializable對象,不能被拿到remote JVM上去用。

            時間有限,只做了Oracle的測試,沒有做其他DB的測試,比如DB2, SQLServer,畢竟這個問題依賴于數據庫,不保證這一做法是用于其它所有數據庫。

    posted on 2008-09-12 12:26 走走停停又三年 閱讀(2087) 評論(1)  編輯  收藏 所屬分類: Weblogic

    FeedBack:
    # re: 關于weblogic中使用prepared statement cache后操作DDL的問題
    2008-09-13 13:54 | 小高
    statement cache 關注一下   回復  更多評論
      
    主站蜘蛛池模板: 青草草色A免费观看在线| 免费国产午夜高清在线视频| 啦啦啦中文在线观看电视剧免费版 | 青青青国产免费一夜七次郎| 亚洲中文久久精品无码1| 亚洲免费观看在线视频| 亚洲精品欧洲精品| 最近免费中文字幕大全高清大全1| 青青草原精品国产亚洲av| 在线观看免费av网站| 亚洲一区中文字幕久久| 永久看日本大片免费35分钟| 亚洲天堂2016| 韩国日本好看电影免费看| 免费夜色污私人影院网站电影| 国产成人毛片亚洲精品| 永久免费av无码网站yy| 久久亚洲美女精品国产精品| 2021国产精品成人免费视频| 亚洲AV无码一区二区三区牲色| 亚洲精品乱码久久久久久蜜桃| 中文字幕一区二区免费| 亚洲精品视频久久| 天天摸夜夜摸成人免费视频| 深夜免费在线视频| 亚洲国产人成网站在线电影动漫 | 成人亚洲性情网站WWW在线观看 | 男人的好免费观看在线视频| 黄色一级毛片免费看| 亚洲va在线va天堂va四虎| 无码一区二区三区免费视频| 日韩精品免费一线在线观看| 亚洲国产综合精品中文第一区| 嫩草影院免费观看| 嫩草影院在线播放www免费观看| 亚洲国产精品综合久久20| 亚洲精品成人在线| 免费观看AV片在线播放| 一级看片免费视频囗交| 亚洲国产韩国一区二区| 国产亚洲精品无码专区|