如何在 ActiveRecord 中为 INSERT ONLY 语句跳过事务? [英] How to skip transaction in ActiveRecord for INSERT ONLY statement?
问题描述
看这个例子:
2.1.3 :001 > Stat.create!
(0.1ms) BEGIN
SQL (0.3ms) INSERT INTO `stats` (`created_at`, `updated_at`) VALUES ('2015-03-16 11:20:08', '2015-03-16 11:20:08')
(0.4ms) COMMIT
=> #<Stat id: 1, uid: nil, country: nil, city: nil, created_at: "2015-03-16 11:20:08", updated_at: "2015-03-16 11:20:08">
如您所见,create!
方法在无用事务中执行插入语句.如何仅在这种情况下禁用转换(而不在整个应用程序中禁用它们)?
As you can see the create!
method execute insert statement inside useless transaction. How to disable transation in this case only (without disabling them in whole application)?
推荐答案
工作原理:
持久化模块定义create
:https://github.com/rails/rails/blob/4-2-stable/activerecord/lib/active_record/persistence.rb#L46
def create!(attributes = nil, &block)
if attributes.is_a?(Array)
attributes.collect { |attr| create!(attr, &block) }
else
object = new(attributes, &block)
object.save!
object
end
end
它创建一个对象并调用#save!
公共api中没有记录,而是调用了https://github.com/rails/rails/blob/4-2-stable/activerecord/lib/active_record/transactions.rb#L290
It is not documented in the public api, but calls https://github.com/rails/rails/blob/4-2-stable/activerecord/lib/active_record/transactions.rb#L290
def save!(*) #:nodoc:
with_transaction_returning_status { super }
end
此时事务包装了保存(超级),它又在持久化模块:https://github.com/rails/rails/blob/4-2-stable/activerecord/lib/active_record/persistence.rb#L141
At this point the transaction wrap the save (super), which is at Persistence module again: https://github.com/rails/rails/blob/4-2-stable/activerecord/lib/active_record/persistence.rb#L141
def save!(*)
create_or_update || raise(RecordNotSaved.new(nil, self))
end
让我们用一些新方法来破解它:
Let's hack this with some new methods:
module ActiveRecord
module Persistence
module ClassMethods
def atomic_create!(attributes = nil, &block)
if attributes.is_a?(Array)
raise "An array of records can't be atomic"
else
object = new(attributes, &block)
object.atomic_save!
object
end
end
end
alias_method :atomic_save!, :save!
end
end
module ActiveRecord
module Transactions
def atomic_save!(*)
super
end
end
end
也许您想使用标准的create!
方法,那么您需要重新定义它.我定义了第一个可选参数 :atomic
,当它出现时意味着您要使用 atomic_save!
方法.
Perhaps you want to use the standard create!
method, then you need to redefine it. I define a first optional parameter :atomic
, and when it's present means you want to use the atomic_save!
method.
module ActiveRecord
module Persistence
module ClassMethods
def create_with_atomic!(first = nil, second = nil, &block)
attributes, atomic = second == nil ? [first, second] : [second, first]
if attributes.is_a?(Array)
create_without_atomic!(attributes, &block)
else
object = new(attributes, &block)
atomic == :atomic ? object.atomic_save! : object.save!
object
end
end
alias_method_chain :create!, :atomic
end
end
end
在 config/initializers/
中使用它可以工作.
With this in config/initializers/<any_name>.rb
it can work.
它如何在控制台上运行:
~/rails/r41example (development) > Product.atomic_create!(name: 'atomic_create')
SQL (99.4ms) INSERT INTO "products" ("created_at", "name", "updated_at") VALUES (?, ?, ?) [["created_at", "2015-03-22 03:50:07.558473"], ["name", "atomic_create"], ["updated_at", "2015-03-22 03:50:07.558473"]]
=> #<Product:0x000000083b1340> {
:id => 1,
:name => "atomic_create",
:created_at => Sun, 22 Mar 2015 03:50:07 UTC +00:00,
:updated_at => Sun, 22 Mar 2015 03:50:07 UTC +00:00
}
~/rails/r41example (development) > Product.create!(name: 'create with commit')
(0.1ms) begin transaction
SQL (0.1ms) INSERT INTO "products" ("created_at", "name", "updated_at") VALUES (?, ?, ?) [["created_at", "2015-03-22 03:50:20.790566"], ["name", "create with commit"], ["updated_at", "2015-03-22 03:50:20.790566"]]
(109.3ms) commit transaction
=> #<Product:0x000000082f3138> {
:id => 2,
:name => "create with commit",
:created_at => Sun, 22 Mar 2015 03:50:20 UTC +00:00,
:updated_at => Sun, 22 Mar 2015 03:50:20 UTC +00:00
}
~/rails/r41example (development) > Product.create!(:atomic, name: 'create! atomic')
SQL (137.3ms) INSERT INTO "products" ("created_at", "name", "updated_at") VALUES (?, ?, ?) [["created_at", "2015-03-22 03:51:03.001423"], ["name", "create! atomic"], ["updated_at", "2015-03-22 03:51:03.001423"]]
=> #<Product:0x000000082a0bb8> {
:id => 3,
:name => "create! atomic",
:created_at => Sun, 22 Mar 2015 03:51:03 UTC +00:00,
:updated_at => Sun, 22 Mar 2015 03:51:03 UTC +00:00
}
警告:您将丢失 after_rollback 和 after_commit 回调!
Caveat: You will lose after_rollback and after_commit callbacks!
注意:在 4.1 上创建的方法!并保存!在模块验证中.On Rails 4.2 处于持久状态.
Note: on 4.1 the methods create! and save! are in module Validations. On Rails 4.2 are in Persistence.
编辑:也许您认为您可以赚取交易经过的时间.在我的示例中,提交时间用于插入(我有一个标准的 HD,我认为你有一个 SSD).
Edit: Perhaps you think you can earn the transaction elapsed time. In my examples the commit time goes to the inserts (I have a standard HD and I think you have an SSD).
这篇关于如何在 ActiveRecord 中为 INSERT ONLY 语句跳过事务?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!