ruby-on-rails - 如何仅在记录不存在时才创建记录,避免重复并且不引发任何错误?

标签 ruby-on-rails postgresql transactions rails-activerecord upsert

众所周知,Model.find_or_create_by(X) 实际上是这样做的:

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

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

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

最佳答案

答案在 doc

Whether that is a problem or not depends on the logic of the application, but in the particular case in which rows have a UNIQUE constraint an exception may be raised, just retry:

begin
  CreditAccount.find_or_create_by(user_id: user.id)
rescue ActiveRecord::RecordNotUnique
  retry
end

解决方案一

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

def self.find_or_create_by(*)
  super
rescue ActiveRecord::RecordNotUnique
  retry
end

用法:Model.find_or_create_by(X)


方案二

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

def self.safe_find_or_create_by(*args, &block)
  find_or_create_by *args, &block
rescue ActiveRecord::RecordNotUnique
  retry
end

用法:Model.safe_find_or_create_by(X)

关于ruby-on-rails - 如何仅在记录不存在时才创建记录,避免重复并且不引发任何错误?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14331926/

相关文章:

java - 在数据库中未找到实体,但存在于持久性上下文中

php - 请问laravel数据库事务锁表吗?

mysql - @transactional、wildfly、mysql(innodb) ejb 不回滚

ruby-on-rails - 为 rails 中的 javascript 生成完整的 url(类似于 javascript_path,但是是 url)

ruby-on-rails - 键值对的正则表达式 Markdown 字符串

jquery - 有没有办法在 IRB 模式下像 Ruby 一样使用 jQuery?

python - 使用 psycopg2 将值从 pandas 系列传递到 PostgreSQL 查询

ruby-on-rails - 使用mongoid使用mongodb在消息父级中嵌入消息回复

ruby-on-rails - rails - fatal error : database "myapp_development" does not exist

function - Postgresql循环函数