在阅读golang SQL /数据库示例中“事务”中“准备”语句的示例时。其中一行说“危险”,但提供的代码示例没有其他选择。
我想对下面的查询有更清晰的解释,因为Wiki页面-http://go-database-sql.org/prepared.html上没有提供太多信息。
tx, err := db.Begin() if err != nil { log.Fatal(err) } defer tx.Rollback() stmt, err := tx.Prepare("INSERT INTO foo VALUES (?)") if err != nil { log.Fatal(err) } defer stmt.Close() // danger! for i := 0; i < 10; i++ { _, err = stmt.Exec(i) if err != nil { log.Fatal(err) } } err = tx.Commit() if err != nil { log.Fatal(err) } // stmt.Close() runs here!
如果您在defer stmt.Close()其中看到提及内容,那是很危险的,但还没有注释掉,请用户删除。
defer stmt.Close()
尽管我在上面的代码中没有看到问题,因为“ defer”将在最后运行代码,但这是否意味着,上面的代码是错误的,应将其替换为下面的代码或其他更好的替代代码。
defer
tx, err := db.Begin() if err != nil { log.Fatal(err) } defer tx.Rollback() stmt, err := tx.Prepare("INSERT INTO foo VALUES (?)") if err != nil { log.Fatal(err) } // Commented out below line. // defer stmt.Close() for i := 0; i < 10; i++ { _, err = stmt.Exec(i) if err != nil { log.Fatal(err) } } err = tx.Commit() if err != nil { log.Fatal(err) } // Comment removed from below line to close the stmt stmt.Close()
我发现上面的两个代码没有什么区别,但是,如果有任何区别或缺少某些内容,我需要上面的专家建议。
一个defer说法是一个很好的方式,以确保运行的东西不管你怎么退出功能。
在这种特殊情况下,这似乎无关紧要,因为所有错误处理程序都使用log.Fatal。如果log.Fatal用return语句替换s 并删除延迟,则现在必须在许多地方进行清理:
log.Fatal
return
tx, err := db.Begin() if err != nil { return nil,err } stmt, err := tx.Prepare("INSERT INTO foo VALUES (?)") if err != nil { tx.Rollback() return nil,err } defer for i := 0; i < 10; i++ { _, err = stmt.Exec(i) if err != nil { tx.Rollback() return nil,err } } err = tx.Commit() if err != nil { stmt.Close() tx.Rollback() return nil,err } stmt.Close() return someValue, nil
如果使用延迟,则很难忘记需要清理的地方。