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