在前文介紹訪問資料庫時介紹了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是在方法即將結束時執行,哪怕是程式碼出現異常也不會影響資料庫連線。