如何在ActiveRecord中跳过用于INSERT ONLY语句的事务? [英] How to skip transaction in ActiveRecord for INSERT ONLY statement?

查看:94
本文介绍了如何在ActiveRecord中跳过用于INSERT ONLY语句的事务?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

看这个例子:

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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆