setAutoCommit(false) sybase SET CHAINED command...異常


在 Sybase ASE 實際應用中,特別是在 ASE + J2EE 應用中,較容易出現 SET CHAINED command not allowed within multi-statement transaction.的異常(儘管到版本 15.0.1 爲止,ASE 並不支持異常機制,但本文爲了方便,統一採用“異常”一詞)。有的開發人員認爲這是 Sybase 數據庫的問題;有的認爲是多次調用 setAutoCommit() 方法的問題;有的開發人員則認爲這是 jConnect 的問題,甚至從 jConnect 的代碼上直接屏蔽此異常。 
  然而,SET CHAINED 異常倒底是怎樣產生的? 
  一、數據庫層 
  首先,讓我們看看 set chained。下面的文字片段摘自《ASE 12.5.2 Reference Manual: Commands》,Page 430: 

chained 
begins a transaction just before the first data retrieval or data modification 
statement at the beginning of a session and after a transaction ends. In 
chained mode, Adaptive Server implicitly executes a begin transaction 
command before the following statements: delete, fetch, insert, lock table, 
open, select, and update. You cannot execute set chained within a transaction. 

  從此段文字可以得知,當 set chained on 後,delete、fetch、insert、lock table、open、select 以及 update 語句將自動啓動一個事務,並要求顯式的完成事務,即明確地調用 commit/rollback。同時,在事務中,不允許設置 chained 模式。 
  下面的 sql 代碼片斷將說明在數據庫層上 SET CHAINED 錯誤信息是如何產生的。 

1> set chained on 
2> go 
1> set chained on 
2> go 
1> begin tran 
2> go 
1> 

  似乎多次調用 set chained 並不會產生異常。接下來, 

1> set chained on 
2> go 
Msg 226, Level 16, State 1: 
Server 'FLYBEAN', Line 1: 
SET CHAINED command not allowed within multi-statement transaction. 
1> set chained off 
2> go 
Msg 226, Level 16, State 1: 
Server 'FLYBEAN', Line 1: 
SET CHAINED command not allowed within multi-statement transaction. 
1> 

  顯然,處於事務環境下,調用 set chained 是會發生異常的,這一點手冊上也非常明確的指出了。但爲什麼前面的片斷中兩次連續調用 set chained 卻不會產生異常呢?請注意文檔上這一句:Adaptive Server implicitly executes a begin transaction command before the following statements: 。 
  重建一個數據庫連接,從頭開始: 

1> set chained on 
2> go 
1> select 1 
2> go 

----------- 


(1 row affected) 
1> set chained on 
2> go 
Msg 226, Level 16, State 1: 
Server 'FLYBEAN', Line 1: 
SET CHAINED command not allowed within multi-statement transaction. 
1> set chained off 
2> go 
Msg 226, Level 16, State 1: 
Server 'FLYBEAN', Line 1: 
SET CHAINED command not allowed within multi-statement transaction. 
1> 

  在執行 select 1 之前,數據庫自動啓動了一筆事務,因此不能再執行 set chained。接下來,完成隱式啓動的事務: 

1> rollback 
2> go 
1> set chained off 
2> go 
1> 

  二、J2EE 層 
  J2EE 應用中,一些輕量級的數據訪問層實現採用 Connection 的setAutoCommit(false) + commit()/rollback() 的方式來管理事務。通過對 jConnect 的反編譯以及對 spt_mda 數據的分析,可以得知 setAutoCommit(true) = SET CHAINED OFF;setAutoCommit(false) = SET CHAINED ON,下圖以順序圖展示調用 setAutoCommit() 方法時,實際發生的交互。 

  另一方面,J2EE 應用中大多采用了連接池。應用在調用 Connection.close() 方法時,實際上並沒有真正地關閉連接,而是將連接回收到池中。假設連接的初態是 chained off。如果應用在取得連接後調用該連接的 setAutoCommit(false) 方法來啓動事務,在事務完成後,通過 close() 方法回到池中時,未能恢復到初始狀態(即 chained off),則應用多次獲取該連接並進行一定操作後,就很有可能出現異常。見下圖: 

  通過上面的分析,理解了產生此異常的原因,就很容易避免此異常,即在完成事務後,關閉連接前,顯式地調用 setAutoCommit(true)。或許有的程序員會認爲麻煩,但別忘記“完壁歸趙”是資源借用者的義務。


---------------------------

環境: 

數據庫:
sybase 15 

連接池:c3p0 


問題: 

獲取Connection之後,設置autoCommit爲false; 

發現使用後連接池沒有釋放鏈接。 

開始以爲是連接池的bug,後來發現在log中,連接池已經釋放鏈接的方法, 

但是沒有釋放成功。 


分析: 

相同的代碼,換成其他的數據庫都沒有問題,時候後連接都能被釋放。 

初步判斷是數據庫的問題。 


解決方案: 


求助完成的谷歌大神,找到解決方案 

使用完Connection後,需要把autoCommit重新設定爲true。 


具體是什麼原因,目前還不清楚 


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