一、屬性
transaction(options = {}, &block)? ?事務(wù)要么同時(shí)成功寥裂,繼續(xù)往下走;要么同時(shí)失敗案疲,回滾封恰。
事務(wù)的4個(gè)屬性(通常被稱為ACID特性):
????原子性:一個(gè)事務(wù)是一個(gè)不可分割的工作單位,事務(wù)中包括的諸操作要么都做褐啡,要么都不做诺舔。
????一致性:事務(wù)必須是使數(shù)據(jù)庫(kù)從一個(gè)一致性狀態(tài)變到另一個(gè)一致性狀態(tài)。一致性與原子性是密切相關(guān)的。
????隔離性:一個(gè)事務(wù)的執(zhí)行不能被其他事務(wù)干擾混萝。即一個(gè)事務(wù)內(nèi)部的操作及使用的數(shù)據(jù)對(duì)并發(fā)的其他事務(wù)是隔離的遗遵,并發(fā)執(zhí)行的各個(gè)事務(wù)之間不能互相干擾。
????持久性:持久性也稱永久性(permanence)逸嘀,指一個(gè)事務(wù)一旦提交车要,它對(duì)數(shù)據(jù)庫(kù)中數(shù)據(jù)的改變就應(yīng)該是永久性的。接下來(lái)的其他操作或故障不應(yīng)該對(duì)其有任何影響崭倘。
事務(wù)中包含多個(gè)不同的model翼岁,通常調(diào)用不同的model。
事務(wù)綁定的是數(shù)據(jù)庫(kù)司光,而非一個(gè)model 對(duì)象琅坡。
只有對(duì)多個(gè)記錄進(jìn)行操作,并且希望這些操作是一個(gè)整體的時(shí)候残家,事務(wù)才有必要榆俺。
Rails提供的事務(wù),可以作為類方法坞淮,也可以作為實(shí)例方法調(diào)用茴晋。
二、事務(wù)回滾
事務(wù)回滾通過(guò)rollback實(shí)現(xiàn)回窘,在rails中诺擅,rollback只會(huì)被一個(gè)Exception觸發(fā)。很多事務(wù)塊中的代碼不會(huì)觸發(fā)異常啡直,即使出錯(cuò)烁涌,也不會(huì)觸發(fā)異常。
如:
這是因?yàn)樵趓ails中酒觅,update_attribute在調(diào)用失敗也不會(huì)觸發(fā)異常撮执,只是簡(jiǎn)單的返回false,必須保證transaction塊中在失敗時(shí)會(huì)拋出異常阐滩。
正確的寫(xiě)法是這樣:
注意:Rails 中約定二打,帶有嘆號(hào)的函數(shù)一般會(huì)在失敗時(shí)拋異常。
find_by, find_by_ 方法在查詢找不到記錄時(shí)掂榔,會(huì)返回nil,find方法在找不到記錄時(shí)症杏,拋出一個(gè)ActiveRecord::RecordNotFound 異常
因?yàn)閒ind_by并不會(huì)拋出異常装获,所以可以修改為:
當(dāng)錯(cuò)誤出現(xiàn)時(shí),事務(wù)本身會(huì)回滾穴豫,同時(shí)異常也會(huì)在外層拋出,因此秤涩,你的調(diào)用方法必須考慮 catch 這個(gè)異常筐眷,并進(jìn)行相應(yīng)的處理习柠。
有一個(gè)特殊的異常,ActiveRecord::Rollback武翎,當(dāng)它被拋出時(shí)溶锭,事務(wù)本身會(huì)回滾,但是它并不會(huì)被重新拋出垫毙,因此你也不需要在外部進(jìn)行 catch 和處理露久。
三欺栗、嵌套事務(wù):
Exception異常:
????拋出Exception異常時(shí),子事務(wù)被回滾消请,同時(shí)类腮,父事務(wù)也能捕獲到此異常,因此缸逃,父事務(wù)也會(huì)被回滾需频。
ActiveRecord::Rollback異常
拋出ActiveRecord::Rollback異常時(shí)筷凤,子事務(wù)被回滾,但是異常不會(huì)被父事務(wù)捕獲挪丢,父事務(wù)可以正常的執(zhí)行乾蓬。
1.rollback回滾
ActiveRecord::Rollback不會(huì)傳播到上層的方法中去巢块,因此這個(gè)例子中父元素不會(huì)接受到子元素拋出的異常。因?yàn)樽邮聞?wù)塊中的內(nèi)容也被合并到了父事務(wù)中去姥闭,因此這兩條 User 記錄都會(huì)被創(chuàng)建越走。
為了保證一個(gè)子事務(wù)的 rollback 被父事務(wù)知曉,必須手動(dòng)在子事務(wù)中添加 :require_new => true 選項(xiàng)铜跑。比如下面的寫(xiě)法:
事務(wù)是跟當(dāng)前的數(shù)據(jù)庫(kù)連接綁定的锅纺,因此肋殴,如果你的應(yīng)用同時(shí)向多個(gè)數(shù)據(jù)庫(kù)進(jìn)行寫(xiě)操作,那么必須把代碼包裹在一個(gè)嵌套事務(wù)中去护锤。比如:
2、Exception異常
?子事務(wù)出現(xiàn)異常驱入,父事務(wù)捕獲到異常亏较,所以兩個(gè)user創(chuàng)建語(yǔ)句都不會(huì)被執(zhí)行掩缓。
四:事務(wù)相關(guān)的回調(diào)
save和destroy方法被自動(dòng)包裹在一個(gè)回調(diào)里,因此相關(guān)的回調(diào) 如:after_save 仍然屬于事務(wù)的一部分,因此回調(diào)代碼時(shí)也有可能被回滾。
因此扁达,如果你希望代碼在事務(wù)外部執(zhí)行的話蠢熄,那么可以使用 after_commit 或者 after_rollback 這樣的回調(diào)函數(shù)