处理事务内的唯一约束异常

时间:2018-06-26 04:54:35

标签: ruby-on-rails ruby postgresql

我有两个要插入的Rails模型/表作为事务的一部分。

例如,以下是我的表格:

  • 帖子:(id)
  • 评论:(id,post_id,comment_text,user_id)
  • 注释者:(id,post_id,user_id),(post_id, user_id)

现在我正尝试等效于:

ActiveRecord::Base.transaction do
  Comment.create!(post: post, user: user, comment_text: '...')

  begin
    Commenters.find_or_create_by!(post: post, user: user)
  rescue PG::UniqueViolation
  end
end

这工作99.9%的时间,但有时两个并发注释将触发PG :: UniqueViolation。

即使我捕获并抑制了PG :: UniqueViolation,由于以下原因,整个事务也会失败:

错误:当前事务中止,命令被忽略,直到事务块结束

我意识到我可以通过加入Post and Comment表来实现这一点,但这是一个简化的示例。

是否有一种更简单的方法来确保两次插入都作为事务的一部分发生,同时又可以忽略该唯一违规,因为我们可以假设记录已经存在?

2 个答案:

答案 0 :(得分:0)

您应该在模型内部正确设置关联,因此Rails会为您进行验证。然后,您可以简单地挽救可能的ActiveRecord::RecordInvalid(带有消息Validation failed: Attribute has already been taken)。

如果您想了解有关唯一性验证的更多信息,这应该派上用场:http://guides.rubyonrails.org/active_record_validations.html#uniqueness

在我看来,实际上您的Commenter模型实际上并不是必需的。它仅包含派生信息,这些信息也可以直接从Comments模型中提取(您已经存储了post_iduser_id),因此可以完全删除Commenter类。然后注意通过设置例如{p>

Comment

但是,这样一来,您将只允许用户评论一次。

我建议您放弃唯一性约束,并通过在class Comment belongs_to :post belongs_to :user validates :user_id, uniqueness: {scope: :post_id} end 模型上进行Commenter选择来构造存储在distinct中的信息。

PS:rails中的模型以大写和单数形式编写,而表(以符号形式)以小写和复数形式引用。

答案 1 :(得分:0)

在事务内部引发的异常有两件事:

  1. 回滚事务,因此事务内部所做的任何更改都不会保留在数据库中。
  2. 异常传播到事务块之外。

因此,您可以将异常处理程序移至transaction调用之外:

begin
  ActiveRecord::Base.transaction do
    Comment.create!(post: post, user: user, comment_text: '...')
    Commenters.find_or_create_by!(post: post, user: user)
  end
rescue PG::UniqueViolation
  retry
end

如果您想提高安全性,可以添加一个计数器仅重试几次。