事務在數據庫應用開發中是相當重要的,尤其是在關系數據庫中。典型的例子就是銀行在轉賬,在兩個賬 戶之間轉賬。
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插件。