【问题标题】:How can i make ruby on rails handle duplicate record errors from MySQL我如何让 ruby​​ on rails 处理来自 MySQL 的重复记录错误
【发布时间】:2011-05-23 04:00:01
【问题描述】:

我的数据库中的多个字段都有一个唯一索引。因此,如果您尝试对重复记录调用 save,它会引发 ActiveRecord::StatementInvalid 并显示 mysql 错误。有没有办法在 Rails 中通过创建唯一约束来处理这个问题,或者在发生这种情况时让它返回相关的错误消息?

这是踪迹:

ActiveRecord::StatementInvalid: Mysql::Error: Duplicate entry '2010-12-09-2-0-1-1' for key 2: INSERT INTO `entries` (`rejected_at`, `created_at`, `comments`, `overtime`, `submitted_at`, `updated_at`, `time`, `approved`, `day`, `user_id`, `approved_at`, `job_id`, `submitted`, `rejected`) VALUES(NULL, '2010-12-09 21:50:46', NULL, 0, NULL, '2010-12-09 21:50:46', 2.0, NULL, '2010-12-09', 1, NULL, 1, NULL, NULL)
 from /home/cmatthews/src/cannon/vendor/rails/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb:219:in `log'
 from /home/cmatthews/src/cannon/vendor/rails/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb:319:in `execute'
 from /home/cmatthews/src/cannon/vendor/rails/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:259:in `insert_sql'
 from /home/cmatthews/src/cannon/vendor/rails/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb:329:in `insert_sql'
 from /home/cmatthews/src/cannon/vendor/rails/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:44:in `insert_without_query_dirty'
 from /home/cmatthews/src/cannon/vendor/rails/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb:18:in `insert'
 from /home/cmatthews/src/cannon/vendor/rails/activerecord/lib/active_record/base.rb:2901:in `create_without_timestamps'
 from /home/cmatthews/src/cannon/vendor/rails/activerecord/lib/active_record/timestamp.rb:53:in `create_without_callbacks'
 from /home/cmatthews/src/cannon/vendor/rails/activerecord/lib/active_record/callbacks.rb:266:in `create'
 from /home/cmatthews/src/cannon/vendor/rails/activerecord/lib/active_record/base.rb:2867:in `create_or_update_without_callbacks'
 from /home/cmatthews/src/cannon/vendor/rails/activerecord/lib/active_record/callbacks.rb:250:in `create_or_update'
 from /home/cmatthews/src/cannon/vendor/rails/activerecord/lib/active_record/base.rb:2538:in `save_without_validation'
 from /home/cmatthews/src/cannon/vendor/rails/activerecord/lib/active_record/validations.rb:1078:in `save_without_dirty'
 from /home/cmatthews/src/cannon/vendor/rails/activerecord/lib/active_record/dirty.rb:79:in `save_without_transactions'
 from /home/cmatthews/src/cannon/vendor/rails/activerecord/lib/active_record/transactions.rb:229:in `send'
 from /home/cmatthews/src/cannon/vendor/rails/activerecord/lib/active_record/transactions.rb:229:in `with_transaction_returning_status'
 from /home/cmatthews/src/cannon/vendor/rails/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:136:in `transaction'
 from /home/cmatthews/src/cannon/vendor/rails/activerecord/lib/active_record/transactions.rb:182:in `transaction'
 from /home/cmatthews/src/cannon/vendor/rails/activerecord/lib/active_record/transactions.rb:228:in `with_transaction_returning_status'
 from /home/cmatthews/src/cannon/vendor/rails/activerecord/lib/active_record/transactions.rb:196:in `save'
 from /home/cmatthews/src/cannon/vendor/rails/activerecord/lib/active_record/transactions.rb:208:in `rollback_active_record_state!'
 from /home/cmatthews/src/cannon/vendor/rails/activerecord/lib/active_record/transactions.rb:196:in `save'

【问题讨论】:

    标签: mysql ruby-on-rails ruby insert unique-constraint


    【解决方案1】:
    begin
      Event.create!(:name => 'Ironman Lanzarote 2011')
    rescue ActiveRecord::RecordNotUnique => e
      # handle duplicate entry 
    end
    

    这适用于我在 Rails 3 下。

    【讨论】:

    • 这违反了最小惊讶原则,也不遵循 Rails 中的约定。
    • 当您使用主/从设置时,您会遇到漂移问题(以及由此产生的竞争条件),使用这种方法可以轻松解决,而不是使用 rails 的独特验证,可能会在从站上检查它。
    • 这很好,但我会将这种行为放在具有描述性名称的模型方法上。
    【解决方案2】:

    您可以为模型添加唯一性约束,以便记录返回错误且无效。

    例如(Rails 2):

    class User < ActiveRecord::Base
      validates_uniqueness_of :email, :scope => [:name, :age]
    end
    

    或(Rails 3)

    class User < ActiveRecord::Base
      validates :email, :uniqueness => {:scope => [:name, :age]}
    end
    

    这应该会产生以下结果:

    user1 = User.create :email => "one@example.com", :name => "one", :age => 20
    user2 = User.create :email => "one@example.com", :name => "one", :age => 20
    user2.valid? # false
    

    【讨论】:

    • 我知道我可以为一个字段执行此操作,但我可以为多个字段执行此操作,因为我可以让它检查它们是否相同:电子邮件、姓名、喜欢的颜色和工作。可以有 5 个名为 john 的人,但只有一个可以拥有 JOhn@example.com、orange 并且是一名程序员。这有意义吗?
    • 嘿,我用多个属性的答案更新了帖子。
    • 虽然涵盖了大多数用例,但 validates_uniqueness_of 中一直存在竞争条件 - 由于其工作方式,它不会完全停止重复条目。
    • 这是正确的奥马尔。您仍然应该在 MySQL 中有一个唯一索引(姓名、年龄、电子邮件),如果您怀疑这可能是您的应用程序的问题,最好用 begin..rescue..end 语句围绕您的更新来捕获这些情况。
    【解决方案3】:

    你能把查询改成

    INSERT IGNORE INTO `entries` ...
    

    或者,您可以考虑INSERT... ON DUPLICATE KEY UPDATE...

    INSERT 文档是 here

    【讨论】:

    • 我认为这会在一定程度上解决问题,但 SQL 查询是自动生成的。我可以改变它,除了我希望它引发一个错误,只是我可以捕获并有一个消息,这只会让服务器说 500 内部服务器错误。
    猜你喜欢
    • 1970-01-01
    • 2012-07-23
    • 2013-01-04
    • 1970-01-01
    • 2021-12-06
    • 1970-01-01
    • 2014-05-13
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多