当前位置:首页-技术前沿-ORM/JPA-ActiveRecord

  • Rails源码研究之ActiveRecord:三,Transactions
    作者: 本站会员  来源:javaEye  发布时间:2007-10-23 13:30:00
  • 这次我们分析一下Rails的事务支持
    1,Rails默认将父子关系的表的save()和destroy()包装在一个事务里(见AWDWR一书的Transactions)
    这保证了父子保存和删除的原子性,即ActiveRecord是级联保存和级联删除的,有源码为证
    transactions.rb:

    1. module ActiveRecord  
       2.   module Transactions  
       3.     def self.included(base)  
       4.       base.extend(ClassMethods)  
       5.       base.class_eval do  
       6.         [:destroy, :save, :save!].each do |method|  
       7.           alias_method_chain method, :transactions  
       8.         end  
       9.       end  
      10.     end  
      11.   
      12.     module ClassMethods  
      13.       def transaction(*objects, &block)  
      14.         previous_handler = trap('TERM') { raise TransactionError, "Transaction aborted" }  
      15.         increment_open_transactions  
      16.   
      17.         begin  
      18.           unless objects.empty?  
      19.             ActiveSupport::Deprecation.warn "Object transactions are deprecated and will be removed from Rails 2.0.  See http://www.rubyonrails.org/deprecation for details.", caller  
      20.             objects.each { |o| o.extend(Transaction::Simple) }  
      21.             objects.each { |o| o.start_transaction }  
      22.           end  
      23.   
      24.           result = connection.transaction(Thread.current['start_db_transaction'], &block)  
      25.   
      26.           objects.each { |o| o.commit_transaction }  
      27.           return result  
      28.         rescue Exception => object_transaction_rollback  
      29.           objects.each { |o| o.abort_transaction }  
      30.           raise  
      31.         ensure  
      32.           decrement_open_transactions  
      33.           trap('TERM', previous_handler)  
      34.         end  
      35.       end  
      36.   
      37.       private  
      38.         def increment_open_transactions #:nodoc:  
      39.           open = Thread.current['open_transactions'] ||= 0  
      40.           Thread.current['start_db_transaction'] = open.zero?  
      41.           Thread.current['open_transactions'] = open + 1  
      42.         end  
      43.   
      44.         def decrement_open_transactions #:nodoc:  
      45.           Thread.current['open_transactions'] -= 1  
      46.         end  
      47.     end  
      48.   
      49.     def transaction(*objects, &block)  
      50.       self.class.transaction(*objects, &block)  
      51.     end  
      52.   end  
      53. end

     

    mysql_adapter.rb:

    1. module ActiveRecord  
       2.   module ConnectionAdapters  
       3.     class MysqlAdapter < AbstractAdapter  
       4.       def begin_db_transaction #:nodoc:  
       5.         execute "BEGIN"  
       6.       rescue Exception  
       7.         # Transactions aren't supported  
       8.       end  
       9.   
      10.       def commit_db_transaction #:nodoc:  
      11.         execute "COMMIT"  
      12.       rescue Exception  
      13.         # Transactions aren't supported  
      14.       end  
      15.   
      16.       def rollback_db_transaction #:nodoc:  
      17.         execute "ROLLBACK"  
      18.       rescue Exception  
      19.         # Transactions aren't supported  
      20.       end  
      21.     end  
      22.   end  
      23. end

    如果我们想给自定义的方法添加事务控制,有如下三种情况:
    1,block transaction

    1. def some_method  
       2.   transaction do  
       3.     david.withdrawal(100)  
       4.     mary.deposit(100)  
       5.   end  
       6. end

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

    2,Object-level transaction

    1. def some_method  
       2.   Account.transaction(from, to) do  
       3.     from.withdraw(100)  
       4.     to.deposit(100)  
       5.   end  
       6. end

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

    3,Across database connections

    1. def some_method  
       2.   Student.transaction do  
       3.     Course.transaction do  
       4.       course.enroll(student)  
       5.       student.units += course.units  
       6.     end  
       7.   end  
       8. end

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

    我们再看看用到transactions的一些地方
    association_collection.rb:

    1. module ActiveRecord  
       2.   module Associations  
       3.     class AssociationCollection < AssociationProxy  
       4.   
       5.       def <<(*records)  
       6.         result = true  
       7.         load_target  
       8.   
       9.         @owner.transaction do  
      10.           flatten_deeper(records).each do |record|  
      11.             raise_on_type_mismatch(record)  
      12.             callback(:before_add, record)  
      13.             result &&= insert_record(record) unless @owner.new_record?  
      14.             @target << record  
      15.             callback(:after_add, record)  
      16.           end  
      17.         end  
      18.   
      19.         result && self  
      20.       end  
      21.   
      22.     end  
      23.   end  
      24. end

    has_many_through_association.rb:  

    1. module ActiveRecord  
       2.   module Associations  
       3.     class HasManyThroughAssociation < AssociationProxy  
       4.   
       5.       def create!(attrs = nil)  
       6.         @reflection.klass.transaction do  
       7.           self << @reflection.klass.with_scope(:create => attrs) { @reflection.klass.create! }  
       8.         end  
       9.       end  
      10.   
      11.     end  
      12.   end  
      13. end

     

     

     

     

     

     

     

     

     

     

     

  • 评论 】 【收藏】 【 推荐给朋友 】 【字体: 】 【关闭
评论:共0条

发表评论:
评论: 
    

Copyright (C) 2005 EasyJF.com 简易java框架网 渝ICP备06004507号
如有意见请与我们联系