简体   繁体   English

如何确定我的ActiveRecord对象是否违反了唯一的数据库密钥/索引?

[英]How can I determine if my ActiveRecord object violates a unique database key/index?

ActiveRecord's validates_uniqueness_of is vulnerable to race conditions . ActiveRecord的validates_uniqueness_of 容易受到竞争条件的影响 To really ensure uniqueness requires additional safeguards. 要真正确保唯一性,需要额外的保护措施。 One suggestion from the ActiveRecord RDocs is to create a unique index on the database, for example by including in your migrations: ActiveRecord RDocs的一个建议是在数据库上创建一个唯一索引,例如通过包含在您的迁移中:

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

This will ensure at the database level that the name is unique. 这将确保在数据库级别名称是唯一的。 But a disadvantage to this approach is that the ActiveRecord::StatementInvalid exception returned on attempting to save a duplicate is not very useful. 但是这种方法的一个缺点是,在尝试保存副本时返回的ActiveRecord::StatementInvalid异常不是很有用。 One cannot be sure when catching this exception that the error was generated by a duplicate record and not just by broken SQL. 在捕获此异常时,无法确定错误是由重复记录生成的,而不仅仅是由损坏的SQL生成的。

One solution, as the RDocs suggest, is to parse the message that comes with the exception and try to detect words like "duplicate" or "unique", but this is kludgy and the message is database backend specific. 正如RDocs所建议的那样,一种解决方案是解析异常附带的消息,并尝试检测诸如“重复”或“唯一”之类的字,但这很麻烦,而且消息是特定于数据库后端的。 For SqlLite3, my understanding is that the message is totally generic and cannot be parsed this way at all. 对于SqlLite3,我的理解是该消息是完全通用的,并且不能以这种方式解析。

Given that this is a fundamental problem for ActiveRecord users, it would be nice to know if there is any standard approach to handling these exceptions. 鉴于这是ActiveRecord用户的一个基本问题,很高兴知道是否有任何标准方法来处理这些异常。 I will offer my suggestion below; 我将在下面提出我的建议; please comment or provide alternatives; 请评论或提供替代方案; thanks! 谢谢!

Parsing the error message is not so bad, but feels kludgy. 解析错误消息并不是那么糟糕,但感觉很糟糕。 A suggestion I ran across (don't remember where) that seems appealing is that in the rescue block you can check the database to see if there is in fact a duplicate record. 我碰到的一个建议(不记得在哪里)似乎很有吸引力,在救援区你可以检查数据库,看看是否有重复的记录。 If there is, then chances are the StatementInvalid is because of the duplicate and you can handle it accordingly. 如果有,那么StatementInvalid可能是因为重复而你可以相应地处理它。 If there isn't, then the StatementInvalid must be from something else, and you must handle it differently. 如果没有,那么StatementInvalid必须来自其他东西,并且您必须以不同方式处理它。

So the basic idea, assuming a unique index on recipe.name as above: 所以基本的想法,假设在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

I attempted to automate this checking with the following: 我尝试使用以下内容自动执行此检查:

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

So now you should be able to use generic_record.violates_unique_index? 所以现在你应该能够使用generic_record.violates_unique_index? in your rescue block to decide how to handle StatementInvalid. 在你的救援块中决定如何处理StatementInvalid。

Hope that is helpful! 希望有所帮助! Other approaches? 其他方法?

Is this really such a big problem? 这真的是个大问题吗?

If you use a unique index together with a validates_uniqueness_of constraint, then 如果您使用唯一索引和validates_uniqueness_of约束,那么

  • Data integrity will be maintained 将保持数据完整性
  • You will at worst only get an error when two separate requests try to insert a non-unique row simultaneously 在两个单独的请求尝试同时插入非唯一行时,最坏的情况是只会出错

So unless you have an app which does many potential duplicate inserts (in which case I would look at redesigning that) I see this rarely being a problem in practice. 因此,除非你有一个应用程序,它可以执行许多潜在的重复插入(在这种情况下我会考虑重新设计),我认为这在实践中很少出现问题。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 ActiveRecord :: RecordNotUnique(PG :: UniqueViolplicate键值违反了唯一约束“ index_users_on_email”的规定:错误:du - ActiveRecord::RecordNotUnique (PG::UniqueViolplicate key value violates unique constraint “index_users_on_email”ation: ERROR: du 如何在不将查询提交到数据库的情况下确定ActiveRecord Relation的原始类 - How can I determine the original class for an ActiveRecord Relation without submitting the query to the database 如何获得独立的ActiveRecord :: Base工具连接到生产数据库? - How can I get my stand alone ActiveRecord::Base tool to connect to my production database? 从Activerecord对象而不是从数据库中删除索引 - Removing Index from Activerecord object Not from Database 如何永久忽略ActiveRecord :: Base类中的数据库列? - How can I permanently ignore a database column in my ActiveRecord::Base class? 如何使用 ActiveRecord 忽略 MySQL 索引? - How can I ignore a MySQL index using ActiveRecord? 确定ActiveRecord对象是否为新 - Determine if ActiveRecord Object is New 如何以最少的中断将ActiveRecord属性重新映射到外键? - How can I remap an ActiveRecord attribute into a foreign key with a minimum of disruption? 如何确保ActiveRecord将更改保存到数据库 - How can I ensure that ActiveRecord is saving changes to the database 如何在Rails / ActiveRecord中的事务之外执行数据库操作 - How can I execute a Database Operation outside of a transaction in Rails / ActiveRecord
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM