假设我们在两个表之间有一个通常的M-M关系,例如:
用户 ---< users_tags > --- 标记。
在这篇文章中,我只关注user_tags,tags的关系:我想避免删除链接的标签。只有未引用的标签才能销毁。
这样做的愚蠢方法是:
class Tag
def before_destroy
unless self.user_tags.empty?
raise "error"
end
end
end
但我认为检查user_tags.empty之间存在潜在的竞争条件?和实际的删除。
第二种方法可能是在检查是否有任何引用之前锁定整个 user_tags 表。
我能想到的第三种方式将涉及对代码的更改,从而创建实际的引用:
在users_tags中添加引用:
然后,before_destroy处理程序可以:
有没有更好的方法来做到这一点?哪一个可靠/最好?我个人倾向于第二个,因为它只需要在before_destroy控制器中使用逻辑,但需要锁定整个表的成本。
修改1:
在尝试使用 LOCK TABLE 时,我意识到他们正在玩我的交易。当使用innodb时,您可以使用事务(及其锁定功能)或使用LOCK / UNLOCK表,两个世界的混合使事情变得更糟(LOCK / UNLOCK导致隐式提交,我错过了doc中的警告)。但这只是为了协议。
(编辑2(几周后):我再次与该问题斗争。所以我想再次强调不要使用LOCK TABLE )
我现在正在尝试在添加子项时对父对象(示例中的标记)使用 SHARE LOCK ),并为删除添加 FOR UPDATE 锁定。但我仍然想知道这是不是这样(在子表中锁定一个Rang以获取父表中的更新)。
顺便说一下。我也意识到这个问题现在完全独立于rails :)。
答案 0 :(得分:3)
避免锁定和检查的一种方法是简单地创建外键。尝试删除另一个表中引用的内容会产生SQL错误。
除此之外,你将不得不进行大量的偏执检查,以确保你没有找出任何必要的标签。
另一种方法是从不同的角度解决问题。例如,作为单个事务清除任何未使用的标记。例如:
DELETE FROM tags WHERE id NOT IN (SELECT DISTINCT(tag_id) FROM users_tags)
这样做的缺点是在模型级别上不执行before_destroy类型的行为,但这对您来说可能不是问题。