事務在數據庫應用開發中是相當重要的,尤其是在關系數據庫中。典型的例子就是銀行在轉賬,在兩個賬 戶之間轉賬。
account1.deposite(100) account2.withdraw(100)
在ActiveRecord中使用transaction方法執行一段block來實現事務。在 block的最後,會提交事務,更新數據庫,如果在block中發生異常,數據庫會回滾所有改變。
Account.transaction do account1.deposite(100) account2.withdraw(100) end
下面是一個完整的例子,我們創建一個表格,有兩個字段:賬號和余額。
定義一個 Account類,包含deposit存錢和withdraw取錢兩個方法。
create_table :accounts, :force => true do |t| t.string :number t.decimal :balance, :precision => 10, :scale => 2, :default => 0 end class Account < ActiveRecord::Base validate :price_must_be_at_least_a_cent def withdarw(amount) adjust_balance_and_save(-amount) end def deposit(amount) adjust_balance_and_save(amount) end def adjust_balance_and_save(amount) self.balance += amount save! end def price_must_be_at_least_a_cent errors.add(:balance, "is negative") if balance < 0 end end peter = Account.create(:balance => 100, :number => "12345") paul = Account.create(:balance => 200, :number => "23456") Account.transaction do paul.deposit(10) peter.withdraw(10) end
我們可以通過
select * from accounts
查詢一下數據,看看賬戶信息是否正確。
peter = Account.create(:balance => 100, :number => "12345")
paul = Account.create(:balance => 200, :number => "23456")
Account.transaction do
paul.deposit(350)
peter.withdraw(350)
end
再來試試異常的情況,再 來查查數據庫,看看數據有沒有回滾到初始值。
在異常的情況下,我們輸出model對象的值看看。
puts "Paul has #{paul.balance}" puts "Peter has #{peter.balance}"
我們會發現雖然數據庫沒有破壞,但是model對象的值被修改 了。這是因為ActiveRecord沒有跟蹤對象的狀態變化,事實上它也做不到。如果在你的應用中這是一個問題的 話,你可以求助於object_transactions插件。