如何在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!
It create an object and call #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!
方法。 / p>
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 /< any_name> .rb
中
它如何在控制台上运行:
~/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
}
Caveat :您将丢失after_rollback和after_commit回调!
Caveat: You will lose after_rollback and after_commit callbacks!
注意:在4.1中,方法创建!并保存!在模块验证中。
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屋!