2009-09-22 95 views
5

我正在研究一個連接到oracle的web應用程序。我們在oracle中有一個表格,其中有一列是「激活的」。任何時候只有一行可以將此列設置爲1。爲了強制執行此操作,我們一直在使用Java中的SERIALIZED隔離級別,但是我們遇到了「無法序列化事務」錯誤,並且無法解決原因。在oracle中READ COMMITTED數據庫隔離級別

我們想知道READ COMMITTED的隔離級別是否可以完成這項工作。所以我的問題是這樣的:

如果我們有涉及以下SQL事務:

SELECT * 
FROM MODEL; 

UPDATE MODEL 
SET ACTIVATED = 0; 

UPDATE MODEL 
SET ACTIVATED = 1 
WHERE RISK_MODEL_ID = ?; 

COMMIT; 

鑑於有可能對這些交易的一個以上的在同一時間被執行,將它多於一個MODEL行可以將激活標誌設置爲1?

任何幫助,將不勝感激。

回答

3

你的解決方案應該工作:你的第一次更新將鎖定整個表。如果另一個事務沒有完成,更新將會等待。您的第二次更新將保證只有一行的值爲1,因爲您正在鎖定表(但它不會阻止INSERT語句)。

您還應該確保存在RISK_MODEL_ID的行(或者在交易結束時,您的零行的值爲'1')。

要防止併發INSERT語句,您需要LOCK表(在獨佔模式下)。

+0

類似的,但我想在獨佔模式下鎖定模型並執行更新模型 SET ACTIVATED = 0 WHERE ACTIVATED = 1 更少的行被更新,所以它的性能稍高一些,並且LOCK TABLE會阻止併發活動在桌子上。 – 2009-09-23 23:06:50

3

你可以考慮使用一個獨特的,基於功能的索引來讓Oracle處理的只是有一排與活化標誌設置爲1

CREATE UNIQUE INDEX MODEL_IX ON MODEL (DECODE(ACTIVATED, 1, 1, NULL)); 

這將阻止具有標誌多行的約束設置爲1,但並不意味着總是有一行將標誌設置爲1.

+0

+1:簡單,高效,多用戶安全 – 2009-09-22 22:36:36

2

如果您想要確保一次只能運行一個事務,那麼您可以使用FOR UPDATE語法。由於你有一個需要鎖定的行,這是一個非常有效的方法。

declare 
    cursor c is 
     select activated 
     from model 
     where activated = 1 
     for update of activated; 
    r c%rowtype; 
begin 
    open c; 
    -- this statement will fail if another transaction is running 
    fetch c in r; 
    .... 
    update model 
    set activated = 0 
    where current of c; 

    update model 
    set activated = 1 
    where risk_model_id = ?; 

    close c; 

    commit; 
end; 
/

commit解鎖。

默認行爲是等待行被釋放。否則,我們可以指定NOWAIT,在這種情況下,嘗試更新當前活動行的任何其他會話都將立即失敗,或者我們可以添加帶有輪詢時間的WAIT選項。 NOWAIT是選擇絕對避免懸掛風險的選項,它還使我們有機會通知用戶其他人正在更新表,他們可能想知道這個表。

此方法比更新表中的所有行更具可擴展性。如WW顯示的那樣使用基於函數的索引來強制只有一行可以具有ACTIVATED = 1的規則。

+0

+1:很好的解決方案 – 2009-09-23 08:05:40

相關問題