package main
import (
"database/sql"
"fmt"
_ "github.com/go-sql-driver/mysql" // 假设使用MySQL,根据实际情况选择驱动
)
func main() {
db, err := sql.Open("mysql", "user:password@tcp(127.0.0.1:3306)/database_name")
if err != nil {
fmt.Println("连接数据库错误:", err)
return
}
defer db.Close()
err = db.Ping()
if err != nil {
fmt.Println("ping数据库错误:", err)
return
}
// 查询示例
rows, err := db.Query("SELECT column1, column2 FROM table_name")
if err != nil {
fmt.Println("查询错误:", err)
return
}
defer rows.Close()
for rows.Next() {
var column1 string
var column2 int
err := rows.Scan(&column1, &column2)
if err != nil {
fmt.Println("扫描结果错误:", err)
return
}
fmt.Printf("column1: %s, column2: %d\n", column1, column2)
}
// 插入示例
result, err := db.Exec("INSERT INTO table_name (column1, column2) VALUES (?,?)", "value1", 123)
if err != nil {
fmt.Println("插入错误:", err)
return
}
lastInsertId, err := result.LastInsertId()
if err != nil {
fmt.Println("获取最后插入ID错误:", err)
return
}
fmt.Printf("最后插入ID: %d\n", lastInsertId)
}
defer语句在这种复杂场景下的优势
- 资源自动释放:确保在函数结束时,无论是否发生错误,数据库连接和查询结果集(如
rows
)等资源都能被正确关闭。避免了因为代码中出现return
语句或者错误导致资源未释放的问题,提高了程序的健壮性。
- 代码简洁:将资源释放的逻辑放在函数末尾的
defer
语句中,使得主逻辑代码更加清晰,减少了手动管理资源释放位置的复杂性,提高了代码的可读性。
defer语句在这种复杂场景下的注意事项
- 执行顺序:
defer
语句是按照后进先出(LIFO)的顺序执行的。在上述代码中,如果有多个defer
语句,它们会按照定义的相反顺序执行。因此在定义defer
语句时,要考虑好资源释放的先后顺序,避免出现资源依赖问题。
- 函数参数求值时机:
defer
语句中的函数参数在defer
语句定义时就会被求值,而不是在defer
语句实际执行时求值。如果参数的值在defer
定义后发生变化,不会影响defer
语句执行时使用的值。例如,如果defer
语句中关闭数据库连接的函数接受一个连接状态的参数,要确保这个参数在defer
定义时能正确反映当前连接状态。
- 错误处理与defer结合:虽然
defer
能确保资源释放,但它并不能替代错误处理。在数据库操作过程中,仍然需要对每一步操作进行错误检查,并在合适的时机返回错误,以保证程序能正确处理异常情况。