defer需求分析
golang中幾乎所有涉及對數據庫的操作都要在回滾操作時用到defer,而當涉及到批量創建/刪除操作的回滾時,就要在for循環內使用defer,雖不推薦使用,但這可以在遍歷其中一條數據失敗時即可回滾止損
func saveDB() error {
ips := []string{"192.168.1.1","192.168.1.2"}
for _, ip := range ips {
if err := db.create(ip); err != nil {
errInfo := fmt.Sprintf("save ip to db error %+v", err)
logCtx.Errorf(errInfo)
return errors.New(errInfo)
}
defer func(ip string) {
if err != nil {
_, errTmp := db.delete(ip)
if errTmp != nil {
errInfo := fmt.Sprintf("delete ip error %v", errTmp)
logCtx.Errorf(errInfo)
}
}
}(ip)
}
}
注意點:每一條for循環遍歷時,defer中第一個判斷條件是err!=nil,這個err是執行存入數據庫的err,而我們執行回滾裏將創建失敗的數據刪除,此時的errTmp必須與err名字錯開,否則若執行回滾內的操作失敗,全局的err還是會進入defer,進入死循環狀態
defer坑
1、defer是先進後出
2、壓入defer中的數據不會隨後續代碼中更改而更改(比如定義i=0,存入defer後續執行i++,但最後defer中的i值仍然是 0)
3、defer可以修改返回值,如果defer中的是i++,最後返回值的i如果是1,執行完defer後,返回值變成2