在 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
(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() 方法回到池中,則當該連接下一次被取出并調用 setAutoCommit(false) 方法時就會拋出異常。見下圖:

通過上面的分析,理解了產生此異常的原因,就很容易避免此異常,即調用了 setAutoCommit(false) 后,必須顯示地完成事務,即使只是執行了一個select語句。同時,關閉連接前,應顯式地調用 setAutoCommit(true)。或許有的程序員會認為麻煩,但別忘記“完壁歸趙”是資源借用者的義務。
posted on 2009-03-02 17:48
歲月如歌 閱讀(1634)
評論(1) 編輯 收藏 所屬分類:
java