如何仅在不存在的情况下创建记录,避免重复并且不会引发任何错误?

907*_*7th 8 postgresql transactions ruby-on-rails upsert rails-activerecord

众所周知,Model.find_or_create_by(X)实际上:

  1. 由X选择
  2. 如果找不到 - >由X创建
  3. 返回记录(找到或创建)

在步骤1和2之间可能存在争用条件.为了避免数据库中的X重复,应该在X的字段集上使用唯一索引.但是如果应用唯一索引,那么竞争事务之一将失败异常(尝试创建X的副本时).

如何实现"安全版本",#find_or_create_by它永远不会引发任何异常并始终按预期工作?

Ben*_*enj 8

答案在文档中

这是否是一个问题取决于应用程序的逻辑,但在行具有UNIQUE约束的特定情况下,可能会引发异常,只需重试:

begin
  CreditAccount.find_or_create_by(user_id: user.id)
rescue ActiveRecord::RecordNotUnique
  retry
end
Run Code Online (Sandbox Code Playgroud)

解决方案1

您可以在模型中实施以下内容,或者如果您需要保持DRY,则可以在关注中实施

def self.find_or_create_by(*)
  super
rescue ActiveRecord::RecordNotUnique
  retry
end
Run Code Online (Sandbox Code Playgroud)

用法: Model.find_or_create_by(X)


解决方案2

或者,如果您不想覆盖find_or_create_by,可以将以下内容添加到模型中

def self.safe_find_or_create_by(*args, &block)
  find_or_create_by *args, &block
rescue ActiveRecord::RecordNotUnique
  retry
end
Run Code Online (Sandbox Code Playgroud)

用法: Model.safe_find_or_create_by(X)