在日志中我看到了
ROLLBACK
,但没有记录任何异常。有没有办法找出导致回滚的原因?
以下是日志摘录:
Phone Load (0.4ms) SELECT "phones".* FROM "phones" WHERE "phones"."id" = $1 LIMIT 1 [["id", 980190963]]
(0.2ms) BEGIN
User Load (0.4ms) SELECT "users".* FROM "users" WHERE "users"."phone_id" = 980190963 LIMIT 1
(0.2ms) ROLLBACK
Phone Load (0.4ms) SELECT "phones".* FROM "phones" WHERE "phones"."id" = $1 LIMIT 1 [["id", 980190963]]
User Load (0.4ms) SELECT "users".* FROM "users" WHERE "users"."phone_id" = 980190963 LIMIT 1
用 BANG '!' 保存记录所以它会产生运行时错误,你知道它发生在哪里
一种方法是手动将信息写入日志。从您的控制器尝试类似的操作:
Rails.logger.info(@your_object.errors.inspect)
这应该输出所有失败的验证的内容。
1) 禁用 before_create、before_save、before_update 并检查它保存当天的位置
2) 如果回滚是由这些方法之一引起的,请在您不打算回滚时检查这些方法是否返回 true。
例如,如果您为布尔字段设置默认值以避免 nil,您可能会这样做
def set_defaults_before_create
self.my_boolean_field ||= false
end
在此示例中,方法 set_defaults_before_create 始终返回 false ,从而回滚您的事务。所以重构它以返回true
def set_defaults_before_create
self.my_boolean_field ||= false
true
end
我想出的3种方法(1种失败)是
在所有相关的保存、验证方法上使用活动记录的观察者
打开活动记录并在触发 ROLLBACK 的位置放置调试器语句,然后运行
caller
来查明触发错误的代码。
失败:覆盖活动记录方法并在异常时暂停。如果我记得这个方法不会捕获任何异常,因为保存方法被包装在事务中。
注意:仅当模式不是 Rails.env.Production? 时才启用。在 Rails 3.2.13 上测试,使用 ruby 1.9.3。
观察者:http://guides.rubyonrails.org/v3.2.13/active_record_validations_callbacks.html#observers
class ActiveRecordObserver < ActiveRecord::Observer
observe "ActiveRecord::Base"
def after_validation(model)
debugger if model.errors.messages.any?
Rails.logger.error "after validation"
end
def before_update(model)
debugger if !model.valid?
Rails.logger.error "before update"
end
def after_update(model)
debugger if !model.valid?
Rails.logger.error "after update"
end
def before_save(model)
debugger if model.errors.messages.any?
Rails.logger.error "#{model}"
Rails.logger.error "before save"
end
def after_save(model)
debugger if model.errors.messages.any?
Rails.logger.error "after save"
end
end
注意:要在最新的 Rails 版本(从 v.4 到最新的 v.6)中使用 Observers,您需要将
gem 'rails-observers'
捆绑在 Gemfile 中(最好包含在 :development 和 :test 组中),将app/observers/active_record_observer.rb
下的类,并通过放置(或添加)以下行来在当前环境配置文件(例如config/environments/development.rb
)中注册观察者:config.active_record.observers = [:active_record_observer]
在执行回滚时放置调试器语句。 https://github.com/rails/rails/blob/3-1-stable/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb#L231
运行
cd bundle show activerecord
找到gem文件夹。
# /Users/<user>/.rvm/gems/ruby-1.9.3-<env>/gems/activerecord-3.2.14/lib/active_record/connection_adapters/abstract/database_statements.rb
196 transaction_open = false
197 decrement_open_transactions
198 if open_transactions == 0
199 rollback_db_transaction
200 debugger
=> 201 rollback_transaction_records(true)
202 else
203 rollback_to_savepoint
204 debugger
205 rollback_transaction_records(false)
当 Rails 服务器或控制台遇到断点时,输入
caller
即可获取回溯。
在开发模式下覆盖 AR。 TODO:仅在 !Rails.env.Production?
时覆盖把这个放进去
app/config/initializers/active_record_or_any_file_name.rb
ActiveRecord::Base.class_eval do
alias_method :old_save, :save
alias_method :old_save!, :save!
def save(*args)
begin
puts "#{self} save"
Rails.logger.info "#{self} save"
old_save(*args)
rescue Exception => e
debugger
puts e
end
end
def save!(*args)
begin
Rails.logger.info "#{self} save!"
puts "#{self} save!"
old_save!(*args)
rescue Exception => e
debugger
puts e
end
end
end
在我的例子中,
has_many
关联是用dependent: :restrict_with_error
而不是dependent: :destroy
定义的。它是在一个问题中定义的,所以我没有立即看到它。不管怎样,如果 Rails 能告诉我们是什么原因导致回滚发生那就太好了:-)