原创作者: hideto   阅读:1570次   评论:0条   更新时间:2011-05-26    
这次我们分析一下Rails的事务支持
1,Rails默认将父子关系的表的save()和destroy()包装在一个事务里(见AWDWR一书的Transactions)
这保证了父子保存和删除的原子性,即ActiveRecord是级联保存和级联删除的,有源码为证
transactions.rb:
module ActiveRecord
  module Transactions
    def self.included(base)
      base.extend(ClassMethods)
      base.class_eval do
        [:destroy, :save, :save!].each do |method|
          alias_method_chain method, :transactions
        end
      end
    end

    module ClassMethods
      def transaction(*objects, &block)
        previous_handler = trap('TERM') { raise TransactionError, "Transaction aborted" }
        increment_open_transactions

        begin
          unless objects.empty?
            ActiveSupport::Deprecation.warn "Object transactions are deprecated and will be removed from Rails 2.0.  See http://www.rubyonrails.org/deprecation for details.", caller
            objects.each { |o| o.extend(Transaction::Simple) }
            objects.each { |o| o.start_transaction }
          end

          result = connection.transaction(Thread.current['start_db_transaction'], &block)

          objects.each { |o| o.commit_transaction }
          return result
        rescue Exception => object_transaction_rollback
          objects.each { |o| o.abort_transaction }
          raise
        ensure
          decrement_open_transactions
          trap('TERM', previous_handler)
        end
      end

      private
        def increment_open_transactions #:nodoc:
          open = Thread.current['open_transactions'] ||= 0
          Thread.current['start_db_transaction'] = open.zero?
          Thread.current['open_transactions'] = open + 1
        end

        def decrement_open_transactions #:nodoc:
          Thread.current['open_transactions'] -= 1
        end
    end

    def transaction(*objects, &block)
      self.class.transaction(*objects, &block)
    end
  end
end


mysql_adapter.rb:
module ActiveRecord
  module ConnectionAdapters
    class MysqlAdapter < AbstractAdapter
      def begin_db_transaction #:nodoc:
        execute "BEGIN"
      rescue Exception
        # Transactions aren't supported
      end

      def commit_db_transaction #:nodoc:
        execute "COMMIT"
      rescue Exception
        # Transactions aren't supported
      end

      def rollback_db_transaction #:nodoc:
        execute "ROLLBACK"
      rescue Exception
        # Transactions aren't supported
      end
    end
  end
end


如果我们想给自定义的方法添加事务控制,有如下三种情况:
1,block transaction
def some_method
  transaction do
    david.withdrawal(100)
    mary.deposit(100)
  end
end

transaction方法后的block里的操作保持原子性

2,Object-level transaction
def some_method
  Account.transaction(from, to) do
    from.withdraw(100)
    to.deposit(100)
  end
end

这种情况下不仅数据库表有事务回滚,对象状态也有事务回滚
不过现在Rails去掉object transactions
如果你仍然想使用object transactions,可以使用object_transactions插件

3,Across database connections
def some_method
  Student.transaction do
    Course.transaction do
      course.enroll(student)
      student.units += course.units
    end
  end
end

但是这样做很难保证不同表的状态,ActiveRecord也不打算做multiple database的transaction,建议不要使用这种方式

我们再看看用到transactions的一些地方
association_collection.rb:
module ActiveRecord
  module Associations
    class AssociationCollection < AssociationProxy

      def <<(*records)
        result = true
        load_target

        @owner.transaction do
          flatten_deeper(records).each do |record|
            raise_on_type_mismatch(record)
            callback(:before_add, record)
            result &&= insert_record(record) unless @owner.new_record?
            @target << record
            callback(:after_add, record)
          end
        end

        result && self
      end

    end
  end
end


has_many_through_association.rb:
module ActiveRecord
  module Associations
    class HasManyThroughAssociation < AssociationProxy

      def create!(attrs = nil)
        @reflection.klass.transaction do
          self << @reflection.klass.with_scope(:create => attrs) { @reflection.klass.create! }
        end
      end

    end
  end
end
评论 共 0 条 请登录后发表评论

发表评论

您还没有登录,请您登录后再发表评论

文章信息

Global site tag (gtag.js) - Google Analytics