在Go语言开发中,操作数据库是常见需求,通过标准库的database/sql包配合对应数据库的驱动,就可以方便地执行SQL语言完成各类数据操作。

环境准备
首先需要安装对应数据库的驱动,这里以常用的MySQL为例,执行以下命令安装驱动:
// 安装MySQL驱动 go get -u github.com/go-sql-driver/mysql
建立数据库连接
使用database/sql包的Open函数建立连接,第一个参数是驱动名称,第二个是连接字符串,连接字符串需要包含用户名、密码、数据库地址和库名等信息。
package main
import (
"database/sql"
"fmt"
_ "github.com/go-sql-driver/mysql" // 匿名导入驱动,触发驱动的init函数注册
)
func main() {
// 连接字符串格式:用户名:密码@tcp(数据库地址:端口)/数据库名?参数
dsn := "root:123456@tcp(127.0.0.1:3306)/test_db?charset=utf8mb4"
db, err := sql.Open("mysql", dsn)
if err != nil {
fmt.Printf("数据库连接失败:%v\n", err)
return
}
// 验证连接是否有效
err = db.Ping()
if err != nil {
fmt.Printf("数据库连接验证失败:%v\n", err)
return
}
fmt.Println("数据库连接成功")
defer db.Close() // 程序结束时关闭连接
}执行SQL语句操作数据
执行增删改操作
对于INSERT、UPDATE、DELETE这类不返回结果集的SQL语句,使用Exec方法执行,建议始终使用参数化查询避免SQL注入问题。
func insertData(db *sql.DB) {
// 参数化查询,?是占位符,对应后面的参数
sqlStr := "INSERT INTO user (name, age) VALUES (?, ?)"
result, err := db.Exec(sqlStr, "张三", 25)
if err != nil {
fmt.Printf("插入数据失败:%v\n", err)
return
}
// 获取插入的自增ID
lastId, err := result.LastInsertId()
if err != nil {
fmt.Printf("获取自增ID失败:%v\n", err)
return
}
// 获取受影响的行数
rowsAffected, err := result.RowsAffected()
if err != nil {
fmt.Printf("获取受影响行数失败:%v\n", err)
return
}
fmt.Printf("插入成功,自增ID:%d,受影响行数:%d\n", lastId, rowsAffected)
}查询单条数据
查询单条数据使用QueryRow方法,通过Scan方法将查询结果映射到变量中。
func querySingle(db *sql.DB) {
var id int
var name string
var age int
sqlStr := "SELECT id, name, age FROM user WHERE id = ?"
// QueryRow返回的是*sql.Row,直接调用Scan方法
err := db.QueryRow(sqlStr, 1).Scan(&id, &name, &age)
if err != nil {
fmt.Printf("查询单条数据失败:%v\n", err)
return
}
fmt.Printf("查询结果:id=%d, name=%s, age=%d\n", id, name, age)
}查询多条数据
查询多条数据使用Query方法,通过遍历返回的*sql.Rows获取每一条数据,最后要关闭Rows释放资源。
func queryMulti(db *sql.DB) {
sqlStr := "SELECT id, name, age FROM user WHERE age > ?"
rows, err := db.Query(sqlStr, 20)
if err != nil {
fmt.Printf("查询多条数据失败:%v\n", err)
return
}
defer rows.Close() // 必须关闭,否则会占用连接
for rows.Next() {
var id int
var name string
var age int
err := rows.Scan(&id, &name, &age)
if err != nil {
fmt.Printf("解析行数据失败:%v\n", err)
return
}
fmt.Printf("id=%d, name=%s, age=%d\n", id, name, age)
}
// 检查遍历过程中是否有错误
if err = rows.Err(); err != nil {
fmt.Printf("遍历行数据错误:%v\n", err)
}
}事务处理
当需要执行多个关联操作时,使用事务保证操作的原子性,要么全部成功,要么全部失败回滚。
func transactionDemo(db *sql.DB) {
// 开启事务
tx, err := db.Begin()
if err != nil {
fmt.Printf("开启事务失败:%v\n", err)
return
}
// 事务执行失败时的回滚逻辑
defer func() {
if p := recover(); p != nil {
tx.Rollback()
panic(p)
} else if err != nil {
tx.Rollback()
}
}()
// 执行事务内的第一个操作
_, err = tx.Exec("UPDATE user SET age = age + 1 WHERE id = ?", 1)
if err != nil {
fmt.Printf("事务操作1失败:%v\n", err)
return
}
// 执行事务内的第二个操作
_, err = tx.Exec("UPDATE user SET age = age - 1 WHERE id = ?", 2)
if err != nil {
fmt.Printf("事务操作2失败:%v\n", err)
return
}
// 提交事务
err = tx.Commit()
if err != nil {
fmt.Printf("提交事务失败:%v\n", err)
return
}
fmt.Println("事务提交成功")
}连接池配置
database/sql内置了连接池,我们可以通过以下方法配置连接池参数,优化数据库操作的性能。
func setPoolConfig(db *sql.DB) {
// 设置连接池最大打开连接数,默认无限制
db.SetMaxOpenConns(10)
// 设置连接池最大空闲连接数,默认2
db.SetMaxIdleConns(5)
// 设置连接可复用的最大时间,超过该时间连接会被关闭
db.SetConnMaxLifetime(time.Hour)
}注意事项
- 所有的查询结果Rows或者Row使用完毕后,要及时调用Close方法释放资源,避免连接泄露
- 执行SQL语句时优先使用参数化查询,不要直接拼接SQL字符串,防止SQL注入攻击
- 驱动使用匿名导入的方式,因为只需要触发驱动的init函数完成注册,不需要直接使用驱动包的其他导出内容
- 连接字符串中的参数要根据实际数据库的配置调整,比如字符集、时区等
SQLGo语言database/sql数据库驱动MySQL修改时间:2026-05-27 23:26:30