原创作者: hideto
阅读:1570次
评论:0条
更新时间:2011-05-26
这次我们分析一下Rails的事务支持
1,Rails默认将父子关系的表的save()和destroy()包装在一个事务里(见AWDWR一书的Transactions)
这保证了父子保存和删除的原子性,即ActiveRecord是级联保存和级联删除的,有源码为证
transactions.rb:
mysql_adapter.rb:
如果我们想给自定义的方法添加事务控制,有如下三种情况:
1,block transaction
transaction方法后的block里的操作保持原子性
2,Object-level transaction
这种情况下不仅数据库表有事务回滚,对象状态也有事务回滚
不过现在Rails去掉object transactions
如果你仍然想使用object transactions,可以使用object_transactions插件
3,Across database connections
但是这样做很难保证不同表的状态,ActiveRecord也不打算做multiple database的transaction,建议不要使用这种方式
我们再看看用到transactions的一些地方
association_collection.rb:
has_many_through_association.rb:
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 条 请登录后发表评论