<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 關注一下   回復  更多評論
      
    主站蜘蛛池模板: 亚洲AV无码精品色午夜在线观看| 亚洲美女大bbbbbbbbb| 三上悠亚在线观看免费| 亚洲avav天堂av在线不卡| 91青青青国产在观免费影视| 亚洲成年网站在线观看| 亚洲精品动漫人成3d在线| 99蜜桃在线观看免费视频网站| 亚洲另类精品xxxx人妖| 免费a级毛片无码av| 暖暖免费日本在线中文| 一本色道久久综合亚洲精品蜜桃冫| 天堂亚洲免费视频| 日韩在线播放全免费| 美女隐私免费视频看| 亚洲导航深夜福利| 久久影视国产亚洲| 在线观看免费高清视频| 99久久成人国产精品免费| 亚洲av永久中文无码精品综合| 亚洲乱码一区二区三区在线观看| 国内免费高清在线观看| 黄桃AV无码免费一区二区三区| 亚洲成人激情小说| 亚洲成年轻人电影网站www | 精品免费国产一区二区三区| 亚洲视频在线免费| 亚洲youwu永久无码精品| 亚洲精品综合一二三区在线 | 国产成人精品免费视频大全五级| 182tv免费视频在线观看| 国产成人高清亚洲一区久久 | 成人网站免费大全日韩国产| 亚洲精品无码久久久久YW| 亚洲日韩在线视频| 亚洲国产另类久久久精品| 免费人成视网站在线观看不卡| 在线天堂免费观看.WWW| 69视频在线观看高清免费| 全黄大全大色全免费大片| 人碰人碰人成人免费视频|