在前文介紹通路資料庫時介紹了
github.com/jmoiron/sqlx
包,本文基于這個包使用資料庫事務。
defer
在使用資料庫事務之前,首先需要了解go語言的defer關鍵字。defer是go語言的延遲執行語句,defer後面的語句會被go進行延遲處理,在函數即将結束的時候,defer後面的語句将逆序執行。也就是說,先defer的語句最後執行。defer很像java或者C#中的finally語句。下面通過一個例子看一下defer。
package main
import "fmt"
func main() {
fmt.Println("defer 開始")
defer fmt.Println(1)
defer fmt.Println(2)
defer fmt.Println(3)
panic("測試 panic")
fmt.Println("defer 結束")
}
代碼運作結果:
defer 開始
3
2
1
panic: 測試 panic
goroutine 1 [running]:
main.main()
/tests/gookokok/main.go:10 +0x1b8
Process finished with exit code 2
通過運作結果可以看到defer的逆序輸出。在之後又手動觸發了一個panic,影響了最後一行的輸出
defer 結束
,go在觸發panic時優先執行了defer,足以證明defer是非常安全的,是以defer也常常被用來互斥解鎖、關閉檔案或資料庫事務的處理。
事務
sqlx使用事務和
database/sql
相比擴充出了MustBegin()、MustExec()等方法,這樣就不需要在代碼中手動處理很多錯誤。MustBegin會在出現錯誤的時候觸發panic而不是傳回錯誤,這樣就可以在代碼的更上一層統一處理錯誤。
tx, err := db.Begin()
err = tx.Exec(...)
err = tx.Commit()
tx := db.MustBegin()
tx.MustExec(...)
err = tx.Commit()
sqlx支援以上兩種開啟事務的方法。MustBegin傳回sqlx.Tx,sqlx.Tx也提供了Select,Get之類的API,執行資料庫操作和使用sqlx.DB是一樣的。
以上代碼執行
tx.MustExec(...)
如果報錯的話,代碼将沒有機會運作到
tx.Commit()
,這樣資料庫連接配接會等到go進行垃圾回收的時候才能關閉,而且很高并發的話,可能會占滿資料庫連接配接數,造成站點無法通路的情況。
tx := db.MustBegin()
defer tx.Rollback()
tx.MustExec(...)
err = tx.Commit()
代碼中加入
defer tx.Rollback()
就可以解決問題。通過前面的介紹已知defer是在方法即将結束時執行,哪怕是代碼出現異常也不會影響資料庫連接配接。
文章出處:基于gin的golang web開發:使用資料庫事務