在學習資料庫時,曾經提到事務,最典型的乙個例項就是銀行轉賬的問題。
a帳戶向b帳戶轉賬100塊,這個事情一定要發生兩件事情,a的帳戶上減去100塊,b的賬戶上加上100塊。如果半路發生問題而中斷這個事情,那麼必須回滾到初始狀態。
下面來看一下用rails來實現這個例子:
首先,建立模型類account, 資料遷移:
在account類中定義方法:
實施資料遷移。執行console
>> peter = account.create(:balance=>100, :number=>"12345")
>> paul = account.create(:balance=>200, :number=>"54321")
#create直接建立並儲存到accounts表中
現在資料庫中的內容#select * from account_development.account
繼續在console裡執行:
>> account.transaction do
?> paul.deposit(10)
>> peter.withdraw(10)
>> end
此時資料庫中的內容:
現在再來看看異常情況,1.從peter帳戶上轉350出去給peter
>> account.transaction do
?> paul.deposit(350)
>> peter.withdraw(350)
>> end
------
這時丟擲了異常:
activerecord::recordinvalid: validation failed: balance is negative!
---資料庫還是保持原樣:
2.從peter賬戶上轉10出去給tom
>> account.transaction do
?> paul.deposit(10)
>> tom.withdraw(10)
>> end
----丟擲異常:
nameerror: undefined local variable or method `tom' for #
資料庫依然保持原樣。
雖然資料庫依然保持原樣,但是模型物件會發生變化,look:
現在還是恢復原狀,peter有100塊,paul有200塊
現在執行:
結果出乎意料,
transfer aborted!
paul has 550.0
peter has -250.0
模型物件已經被改變了!
原因就在於activerecord並沒有跟蹤物件在事務前後的狀態,實際上它也跟蹤不了, 因為沒有一種簡單的辦法可以知道哪些模型物件參與了事務。為了解決這個問題,我們可以把涉及一次事務的模型物件以引數的形式明確的告訴transaction
這次結果就跟我們料想的一樣了:
transfer aborted!
paul has 200.0
peter has 100.0
現在可以將轉賬的方法寫到account類中去了,一次轉賬涉及兩個帳戶, 並且不是由其中的任何乙個來發起的, 所以這個方法時乙個類方法, 接受兩個account物件作為引數:
試試在console下使用這個方法:
>> peter=account.find(1)
>> paul=account.find(2)
>> account.transfer(peter, paul, 350) rescue puts "transfer aborted!"
#結果:
#transfer aborted!
=> nil
------
>> puts "paul has #"
#結果paul has 200.0
=> nil
------
>> puts "peter has #"
#結果:
peter has 100.0
=> nil
-------
讓事務自動恢復物件狀態也有乙個缺點:你將無法獲知驗證過程**現的錯誤資訊,非法的物件不會被儲存,事務會將所有修改回滾,但沒有什麼簡單方法可以知道究竟**出了錯。
事務的實現
在很早的以前,我們要實現乙個事務通常是基於sql的資料庫事務,一般的通過sql查詢語言來實現,如下所示,同時更新兩本書的 begin transaction update tb book set price 122 where ident current 1001 update tb book se...
jedis實現redis事務方法exec返回空陣列
正題 先說我發現的問題 當乙個事務的執行被打斷,jedis的exec 為什麼沒有返回null,返回的是乙個empty list?static void rename final k key,final k newkey,redisoperationsoperations else while ope...
rails實現驗證碼
網上其實有一大堆這樣的資料了,我再寫也沒多大價值,談下幾個注意點吧。1.在windows上安裝rmagic,如果你是通過gem安裝的,require rmagic 要修改為 require rubygems require rmagick 才能正確引入。2.網上那個例子,畫布是使用rmagic內建的...