【问题标题】:How can I determine if my ActiveRecord object violates a unique database key/index?如何确定我的 ActiveRecord 对象是否违反了唯一的数据库键/索引?
【发布时间】:2010-10-17 23:43:26
【问题描述】:

ActiveRecord 的validates_uniqueness_ofvulnerable to race conditions。要真正确保唯一性,需要额外的保障措施。 ActiveRecord RDocs 的一个建议是在数据库上创建一个唯一索引,例如通过包含在您的迁移中:

add_index :recipes, :name, :unique => true

这将确保在数据库级别名称是唯一的。但是这种方法的一个缺点是尝试保存副本时返回的ActiveRecord::StatementInvalid 异常不是很有用。在捕获此异常时,无法确定该错误是由重复记录产生的,而不仅仅是由损坏的 SQL 产生的。

正如 RDocs 建议的那样,一种解决方案是解析伴随异常而来的消息,并尝试检测诸如“重复”或“唯一”之类的词,但这很笨拙,并且该消息是特定于数据库后端的。对于SqlLite3,我的理解是消息是完全通用的,根本不能这样解析。

鉴于这对 ActiveRecord 用户来说是一个基本问题,如果有任何标准方法来处理这些异常,那就太好了。我将在下面提出我的建议;请发表评论或提供替代方案;谢谢!

【问题讨论】:

    标签: ruby-on-rails database exception activerecord indexing


    【解决方案1】:

    解析错误消息并没有那么糟糕,但感觉很笨拙。我遇到的一个似乎很吸引人的建议(不记得在哪里)是,在救援块中,您可以检查数据库以查看实际上是否存在重复记录。如果有,那么 StatementInvalid 很可能是因为重复,您可以相应地处理它。如果没有,那么 StatementInvalid 必须来自其他东西,您必须以不同的方式处理它。

    所以基本思想,假设recipe.name上的唯一索引如上:

    begin
      recipe.save!
    rescue ActiveRecord::StatementInvalid
      if Recipe.count(:conditions => {:name => recipe.name}) > 0
        # It's a duplicate
      else
        # Not a duplicate; something else went wrong
      end
    end
    

    我尝试通过以下方式自动执行此检查:

    class ActiveRecord::Base
      def violates_unique_index?(opts={})
        raise unless connection
        unique_indexes = connection.indexes(self.class.table_name).select{|i|i.unique}
        unique_indexes.each do |ui|
          conditions = {}
          ui.columns.each do |col|
            conditions[col] = send(col)
          end
          next if conditions.values.any?{|c|c.nil?} and !opts[:unique_includes_nil]
          return true if self.class.count(:conditions => conditions) > 0
        end
        return false
      end
    end
    

    所以现在您应该可以在救援块中使用generic_record.violates_unique_index? 来决定如何处理 StatementInvalid。

    希望对您有所帮助!其他方法?

    【讨论】:

    • 我实现了这个并且似乎完成了工作。很想去掉 validates_uniqueness,因为这基本上可以更有效地做同样的事情。将发布任何相关更新。
    • 一个非常小的挑剔:如果在获取异常和查询重复项之间删除了重复项,则会得到错误的错误。
    【解决方案2】:

    真的有这么大的问题吗?

    如果您将唯一索引与validates_uniqueness_of 约束一起使用,那么

    • 将保持数据完整性
    • 在最坏的情况下你会 只有当两个分开时才会出错 requests 尝试插入一个非唯一的 同时行

    因此,除非您有一个应用程序可以执行许多潜在的重复插入(在这种情况下,我会考虑重新设计它)我认为这在实践中很少成为问题。

    【讨论】:

    • 对,你只会很少得到异常。但我仍然认为处理它会很好:)。来自您的回答的一个问题:如果我可以避免它,我什至想使用validates_uniqueness_of?如果我的回答有效,我会很想至少抛出一个 :if 就可以了...
    猜你喜欢
    • 1970-01-01
    • 2023-03-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-12-03
    • 2017-04-28
    • 2018-03-24
    相关资源
    最近更新 更多