跳转到主内容
趣航编程网 - 趣学编程,启航技术之路!

Golang使用database/sql进行基础CRUD

必须先调用sql.Open获取*sql.DB但不立即建连,首次Query/Exec才触发连接;需导入驱动、检查Open和Ping的error;Query用于多行、QueryRow用于单行;Exec执行增删改并获取结果;事务须显式Commit/Rollback。 如何用
database/sql
连接数据库并执行查询 必须先调用
sql.Open
获取
*sql.DB
,但它不立即建连;真正触发连接的是第一次执行查询(如
Query
或
Exec
)。常见错误是忽略返回的
error
,导致后续操作 panic。
sql.Open
的第一个参数是驱动名(如
"mysql"
、
"postgres"
),需提前导入对应驱动(如
_ "github.com/go-sql-driver/mysql"
) 第二个参数是数据源字符串(DSN),格式因驱动而异:
"user:pass@tcp(127.0.0.1:3306)/dbname?parseTime=true"
务必调用
db.Ping()
主动验证连接是否可用,否则首次查询失败时才暴露问题
db, err := sql.Open("mysql", "root:@tcp(localhost:3306)/test") if err != nil { log.Fatal(err) } if err := db.Ping(); err != nil { log.Fatal("failed to connect:", err) }
Query
和
QueryRow
的区别与选用场景
Query
用于返回多行结果(如 SELECT 多条记录),返回
*sql.Rows
;
QueryRow
专为「预期只有一行」设计(如 SELECT ... LIMIT 1 或 聚合函数 ),自动调用
Scan
并处理
sql.ErrNoRows
。 用
QueryRow
时,即使 SQL 本意是查一行,若实际无结果,
Scan
会返回
sql.ErrNoRows
,不是
nil
Query
必须显式调用
rows.Close()
,否则连接不会归还到连接池——这是最常被漏掉的资源泄漏点 不要对同一
*sql.Rows
多次调用
Scan
;每次
Next()
移动游标,需在循环中配对使用
var name string err := db.QueryRow("SELECT name FROM users WHERE id = ?", 123).Scan(&name) if err == sql.ErrNoRows { // 处理未找到 } else if err != nil { // 处理其他错误 }
插入、更新、删除必须用
Exec
,不能用
Query
Exec
专门处理不返回结果集的操作(INSERT/UPDATE/DELETE),它返回
sql.Result
,可获取影响行数或最后插入 ID;用
Query
执行这类语句虽可能不报错,但会浪费连接、阻塞连接池,且无法获取执行结果。
Exec
不支持查询缓存,也不走查询分析器优化路径,纯命令执行 获取自增 ID 要用
result.LastInsertId()
,但注意:SQLite 和某些 MySQL 配置下该值可能不可靠,PostgreSQL 则根本不支持,得改用
RETURNING
子句配合
QueryRow
批量插入别拼 SQL 字符串,用
VALUES (?, ?), (?, ?)
占位符 + 多参数传入,避免 SQL 注入和语法错误
res, err := db.Exec("INSERT INTO users(name, email) VALUES(?, ?)", "Alice", "a@example.com") if err != nil { log.Fatal(err) } id, _ := res.LastInsertId() // 注意:PostgreSQL 不适用
事务处理必须显式
Commit
或
Rollback
db.Begin()
返回的
*sql.Tx
是独立连接,所有操作都绑定在其上;忘记
Commit
或
Rollback
会导致连接长期占用、锁表、甚至连接池耗尽。 go语言参考手册 中文CHM版 Go 是一个开源的编程语言,它能让构造简单、可靠且高效的软件变得容易。本文给大家带来Go参考手册,需要的可以来下载! Go是从2007年末由Robert Griesemer, Rob Pike, Ken Thompson主持开发,后来还加入了Ian Lance Taylor, Russ Cox等人,并最终于2009年11月开源,在2012年早些时候发布了Go 1稳定版本。现在Go的开发已经是完全开放的,并且拥有一个活跃的社区。 Go 语言特色 简洁、快速、安全 并行、有趣、开源 内存管理、v数组安全、编译 下载 立即学习 “ go语言免费学习笔记(深入) ”; 推荐用
defer tx.Rollback()
开头,再在成功路径末尾
tx.Commit()
—— 这样能确保无论中间哪步出错,事务都会回滚 事务内不能再用原始
*sql.DB
执行语句,否则会脱离事务上下文 事务超时由驱动和数据库决定,
database/sql
本身不提供超时控制,需靠上下文(
context.WithTimeout
)传入
BeginTx
tx, err := db.Begin() if err != nil { return err } defer tx.Rollback()

_, err = tx.Exec("UPDATE accounts SET balance = balance - ? WHERE id = ?", 100, 1) if err != nil { return err } return tx.Commit()

事务的边界容易被忽视:比如在函数里开了事务,但调用方没处理 panic,
defer
就不会触发
Rollback
;更稳妥的做法是把
Rollback
放在 recover 之后,或者统一用带 context 的
BeginTx
配合超时兜底。

相关文章