Golang初学者操作MySQL实现CRUD功能指南使用Go语言的database/sql包配合MySQL驱动实现CRUD操作是学习Go数据库编程的基础。以下是详细实现步骤和注意事项:
一、安装依赖和连接数据库
1. 安装MySQL驱动go get -u github.com/go-sql-driver/mysql2. 导入包import ( "database/sql" _ "github.com/go-sql-driver/mysql" // 匿名导入注册驱动 "log")3. 创建数据库连接dsn := "user:password@tcp(127.0.0.1:3306)/dbname"db, err := sql.Open("mysql", dsn)if err != nil { log.Fatal(err)}defer db.Close() // 程序退出时关闭连接// 测试连接if err := db.Ping(); err != nil { log.Fatal(err)}// 设置连接池参数db.SetMaxOpenConns(20) // 最大打开连接数db.SetMaxIdleConns(10) // 最大空闲连接数关键点:
- sql.Open仅初始化连接池,不立即建立连接
- 必须使用db.Ping()验证连接
- 合理设置连接池参数避免资源耗尽
二、查询数据(Read)
1. 基本查询rows, err := db.Query("SELECT id, name FROM users WHERE age > ?", 18)if err != nil { log.Fatal(err)}defer rows.Close() // 必须关闭rows释放资源for rows.Next() { var id int var name string if err := rows.Scan(&id, &name); err != nil { log.Fatal(err) } fmt.Println(id, name)}// 检查遍历过程中是否有错误if err = rows.Err(); err != nil { log.Fatal(err)}2. 单行查询var name stringerr := db.QueryRow("SELECT name FROM users WHERE id = ?", 1).Scan(&name)if err != nil { if err == sql.ErrNoRows { fmt.Println("无结果") } else { log.Fatal(err) }}注意事项:
- 必须调用rows.Close()
- Scan参数顺序和类型必须与查询结果匹配
- 使用?作为参数占位符防止SQL注入
三、插入数据(Create)
1. 基本插入result, err := db.Exec( "INSERT INTO users (name, email) VALUES (?, ?)", "Tom", "tom@example.com",)if err != nil { log.Fatal(err)}lastID, err := result.LastInsertId()if err != nil { log.Fatal(err)}fmt.Println("Last inserted ID:", lastID)2. 使用结构体绑定(推荐)type User struct { Name string Email string}func insertUser(db *sql.DB, user User) (int64, error) { result, err := db.Exec( "INSERT INTO users (name, email) VALUES (?, ?)", user.Name, user.Email, ) if err != nil { return 0, err } return result.LastInsertId()}最佳实践:
- 字段较多时使用结构体管理参数
- 总是检查Exec返回的错误
- 获取LastInsertId确认插入成功
四、更新和删除(Update & Delete)
1. 更新操作res, err := db.Exec( "UPDATE users SET name = ? WHERE id = ?", "Jerry", 1,)if err != nil { log.Fatal(err)}rowsAffected, err := res.RowsAffected()if err != nil { log.Fatal(err)}fmt.Println("Rows affected:", rowsAffected)2. 删除操作res, err := db.Exec("DELETE FROM users WHERE id = ?", 1)if err != nil { log.Fatal(err)}rowsAffected, _ := res.RowsAffected()fmt.Println("Deleted rows:", rowsAffected)安全提示:
- DELETE操作前建议先SELECT确认
- 重要数据建议使用软删除(添加is_deleted字段)
- 测试环境使用事务防止误删
五、事务处理
tx, err := db.Begin()if err != nil { log.Fatal(err)}defer func() { if p := recover(); p != nil { tx.Rollback() panic(p) // 重新抛出panic } else if err != nil { tx.Rollback() } else { err = tx.Commit() }}()_, err = tx.Exec("UPDATE accounts SET balance = balance - ? WHERE id = ?", 100, 1)if err != nil { log.Fatal(err)}_, err = tx.Exec("UPDATE accounts SET balance = balance + ? WHERE id = ?", 100, 2)if err != nil { log.Fatal(err)}事务要点:
- 使用db.Begin()启动事务
- 通过defer实现自动回滚或提交
- 每个操作后检查错误
六、常见问题处理
1. 错误处理// 查询错误示例rows, err := db.Query("SELECT * FROM non_existent_table")if err != nil { if mysqlErr, ok := err.(*mysql.MySQLError); ok { fmt.Printf("MySQL错误: %d %sn", mysqlErr.Number, mysqlErr.Message) } else { log.Fatal(err) }}2. 并发控制var wg sync.WaitGroupfor i := 0; i < 10; i++ { wg.Add(1) go func(id int) { defer wg.Done() // 使用db连接池自动管理连接 _, err := db.Exec("INSERT INTO users (name) VALUES (?)", fmt.Sprintf("user%d", id)) if err != nil { log.Printf("goroutine %d error: %v", id, err) } }(i)}wg.Wait()3. 性能优化- 开启MySQL慢查询日志
- 使用EXPLAIN分析SQL执行计划
- 合理设置连接池参数
- 考虑使用sqlx等扩展库简化操作
七、完整示例
package mainimport ( "database/sql" "fmt" "log" _ "github.com/go-sql-driver/mysql")type User struct { ID int Name string Email string}func main() { // 初始化连接 dsn := "root:password@tcp(127.0.0.1:3306)/testdb" db, err := sql.Open("mysql", dsn) if err != nil { log.Fatal(err) } defer db.Close() // 测试连接 if err := db.Ping(); err != nil { log.Fatal(err) } // 创建表 _, err = db.Exec(` CREATE TABLE IF NOT EXISTS users ( id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(50) NOT NULL, email VARCHAR(100) NOT NULL UNIQUE ) `) if err != nil { log.Fatal(err) } // 插入数据 result, err := db.