我正在开发一个连接到oracle的网络应用。我们在oracle中有一个表“激活”列。任何时候只有一行可以将此列设置为1。为了强制执行此操作,我们一直在Java中使用SERIALIZED隔离级别,但是我们遇到了“无法序列化事务”错误,并且无法解决原因。
我们想知道READ COMMITTED的隔离级别是否可以完成这项工作。所以我的问题是:
如果我们有一个涉及以下SQL的事务:
SELECT *
FROM MODEL;
UPDATE MODEL
SET ACTIVATED = 0;
UPDATE MODEL
SET ACTIVATED = 1
WHERE RISK_MODEL_ID = ?;
COMMIT;
鉴于这些事务中的多个事务可能同时执行,多个MODEL行是否可以将激活标志设置为1?
任何帮助都将不胜感激。
答案 0 :(得分:3)
您的解决方案应该有效:您的第一次更新将锁定整个表格。如果另一个事务未完成,则更新将等待。您的第二次更新将保证只有一行的值为1,因为您正在锁定表(但它不会阻止INSERT语句)。
您还应该确保存在RISK_MODEL_ID
的行(或者您在事务结束时将值为'1'的行为零)。
为了防止并发INSERT语句,你会LOCK表(在EXCLUSIVE MODE中)。
答案 1 :(得分:3)
你可以考虑使用一个独特的,基于函数的索引让Oracle处理只有一行激活标志设置为1的约束。
CREATE UNIQUE INDEX MODEL_IX ON MODEL ( DECODE(ACTIVATED, 1, 1, NULL));
这将停止将一个标志设置为1的多行,但这并不意味着总是有一行将标志设置为1.
答案 2 :(得分: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的规则。