如何使用Golang操作SQLite数据库_Golang database/sql基础操作示例
技术百科
P粉602998670
发布时间:2026-01-26
浏览: 次 需先安装 mattn/go-sqlite3 驱动并下划线导入以注册,DSN 支持 WAL 模式等参数;QueryRow 用于单行查询,Query 用于多行遍历且须 Close;Exec 后可用 LastInsertId 和 RowsAffected 获取 ID 与影响行数;事务中 Rollback 必须 defer 且不可忽略错误。
如何用 database/sql 连接 SQLite 并执行查询
Go 标准库的 database/sql 本身不包含 SQLite 驱动,必须搭配第三方驱动(如 mattn/go-sqlite3)才能工作。直接调用 sql.Open("sqlite3", "test.db") 会 panic,报错 sql: unknown driver "sqlite3"。
实操要点:
- 先
go get github.com/mattn/go-sqlite3,注意该包含 C 代码,需系统有gcc(Windows 用户推荐用gcc.exe而非 MSVC) -
import _ "github.com/mattn/go-sqlite3"—— 下划线导入是关键,它触发驱动注册 - DSN 支持常见参数:
test.db?_journal_mode=WAL&_busy_timeout=5000,其中_journal_mode影响并发写入行为,_busy_timeout单位为毫秒 - 连接成功后,
*sql.DB是长期复用对象,不要每次操作都Open/Close
package main
import (
"database/sql"
"log"
_ "github.com/mattn/go-sqlite3"
)
func main() {
db, err := sql.Open("sqlite3", "example.db")
if err != nil {
log.Fatal(err)
}
defer db.Close()
_, err = db.Exec("CREATE TABLE IF NOT EXISTS users(id INTEGER PRIMARY KEY, name TEXT)")
if err != nil {
log.Fatal(err)
}
}
QueryRow 和 Query 的区别与选型场景
QueryRow 用于预期**至多一行结果**的语句(如 SELECT ... LIMIT 1 或主键查询),它自动调用 Scan 并返回单行错误;Query 返回 *sql.Rows,适用于多行遍历,必须显式 rows.Close() 否则可能泄漏连接。
常见踩坑点:
- 对
QueryRow忘记调用Scan:不会报错,但变量保持零值 - 用
Query查单行却没检查rows.Next():可能 panic 或静默跳过数据 -
rows.Scan传参必须是指针,且列数、类型须与 SELECT 完全匹配,否则报sql: Scan error on column index 0
var name string
err := db.QueryRow("SELECT name FROM users WHERE id = ?", 1).Scan(&name)
if err == sql.ErrNoRows {
// 处理不存在的情况
} else if err != nil {
log.Fatal(err)
}
rows, err := db.Query("SELECT id, name FROM users WHERE id > ?", 0)
if err != nil {
log.Fatal(err)
}
defer rows.Close()
for rows.Next() {
var id int
var n string
if err := rows.Scan(&id, &n); err != nil {
log.Fatal(err)
}
log.Printf("id=%d, name=%s", id, n)
}
使用 Exec 插入/更新时如何获取自增 ID 和影响行数
SQLite 的 INSERT 语句执行后,可通过 Result.LastInsertId() 获取新记录的主键(仅当主键是 INTEGER PRIMARY KEY 类型时有效);Result.RowsAffected() 返回受更改的行数,对 INSERT 总是 1,但对 UPDATE / DELETE 可能为 0(无匹配)。
注意:
-
LastInsertId()在 SQLite 中底层调用sqlite3_last_insert_rowid(),只对当前连接最近一次INSERT有效 - 如果表没有整型主键,或用了
WITHOUT ROWID,LastInsertId()返回 0 -
RowsAffected()在 SQLite 驱动中默认启用,但某些旧版驱动需在 DSN 加_pragma=journal_mode(WAL)才稳定返回
res, err := db.Exec("INSERT INTO users(name) VALUES (?)", "alice")
if err != nil {
log.Fatal(err)
}
id, err := res.LastInsertId()
if err != nil {
log.Fatal(err)
}
log.Printf("inserted id: %d", id)
cnt, err := res.RowsAffected()
if err != nil {
log.Fatal(err)
}
log.Printf("affected rows: %d", cnt)
事务处理中 Rollback 必须配 defer 且不能忽略错误
SQLite 的事务默认是 auto-commit 模式,显式事务需用 db.Begin() 启动,之后所有操作都在该事务上下文中。关键原则是:只要 tx.Commit() 没成功,就必须调用 tx.Rollback(),否则连接会卡在事务状态,后续操作可能被阻塞或报 database is locked。
典型误写:
- 把
defer tx.Rollback()写在tx.Commit()之后 → 永远不会执行回滚 - 只在
if err != nil分支里调用Rollback(),但忘记defer→ 函数 panic 时无法回滚 - 忽略
Rollback()的返回值:它也可能出错(比如连接已断),应至少打日志
tx, err := db.Begin()
if err != nil {
log.Fatal(err)
}
defer func() {
if r := recover(); r != nil {

tx.Rollback()
panic(r)
}
}()
_, err = tx.Exec("INSERT INTO users(name) VALUES (?)", "bob")
if err != nil {
tx.Rollback()
log.Fatal(err)
}
err = tx.Commit()
if err != nil {
log.Fatal(err)
}
事务真正难处理的地方不在语法,而在“什么时候该 rollback”——比如中间调用了外部 HTTP 请求失败,是否回滚?这取决于业务一致性要求,database/sql 不帮你做这个判断。
# ai
# 都在
# 而在
# 则是
# windows
# 适用于
# 什么时候
# 下划线
# win
# auto
# http
# go
# golang
# Error
# 并发
# 对象
# if
# 区别
# 标准库
# 指针
# nil
# 数据库
# 报错
# git
# github
# delete
# select
# 遍历
# sql
# 行数
# 主键
# 整型
# database
# column
# Integer
# sqlite
相关栏目:
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
AI推广<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
SEO优化<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
技术百科<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
谷歌推广<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
百度推广<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
网络营销<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
案例网站<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
精选文章<?muma echo $count; ?>
】
相关推荐
- php转mp4怎么保留字幕_php处理带字幕视频转
- php订单日志权限怎么设_php订单日志文件权限设
- c++如何使用std::bind绑定函数参数_c+
- Mac系统更新下载慢或失败怎么办_解决macOS升
- Win11怎么更改鼠标指针方案_Windows11
- Windows 10怎么把任务栏放在屏幕上方_Wi
- 如何在Golang中写入XML文件_生成符合规范的
- php怎么捕获异常_trycatch结构处理运行时
- Python代码测试策略_质量保障解析【教程】
- Windows系统被恶意软件破坏后的恢复策略_错误
- Win11怎么设置屏保_Windows 11屏幕保
- Win10如何更改用户账户控制_Windows10
- 如何在 Go 开发中正确处理本地包导入与远程模块路
- 如何在 Python 中将 ISO 8601 时间
- 如何使用Golang开发简单的聊天室消息存储_Go
- Win11声音太小怎么办_Windows 11开启
- Win11怎么关闭自动调节屏幕亮度_Windows
- 如何使用Golang实现基本类型比较_Golang
- Win10怎么卸载迅雷_Win10彻底卸载迅雷方法
- php做exe支持多线程吗_并发处理实现方式【详解
- Windows10如何更改任务栏高度_Win10解
- 如何使用Golang实现容器健康检查_监控和自动重
- c++中如何使用auto关键字_c++11类型推导
- c++中的std::conjunction和std
- ACF 教程:如何正确更新嵌套在多层 Group
- Win11局域网共享怎么设置 Win11文件夹网络
- Win11如何卸载OneDrive_Win11卸载
- php8.4新语法match怎么用_php8.4m
- c++获取当前时间戳_c++ time函数使用详解
- 如何使用Golang实现微服务事件驱动_使用消息总
- Python项目维护经验_长期演进说明【指导】
- 如何使用Golang log记录不同级别日志_Go
- php打包exe怎么传递参数_命令行参数接收方法【
- Go 中 defer 语句在 goroutine
- Win11怎么关闭透明效果_Windows11辅助
- c++中如何使用std::variant_c++1
- 当网站SEO排名下降时,如何应对?
- 如何使用Golang管理跨项目依赖_Golang多
- Win11如何设置开机自动联网 Win11宽带连接
- Windows10系统怎么查看硬盘健康_Win10
- php打包exe后无法写入文件_权限问题解决方法【
- Win11怎么关闭VBS安全性_Windows11
- Go语言中slice追加操作的底层共享机制详解
- Win11如何暂停系统更新 Win11暂停更新最长
- Win11如何更新显卡驱动 Win11检查和安装设
- Win11怎样彻底卸载自带应用_Win11彻底卸载
- PHP主流架构如何做单元测试_工具与流程【详解】
- Go 语言标准库为何不提供泛型 Contains
- Python正则表达式实战_模式匹配说明【教程】
- 如何使用Golang实现负载均衡_分发请求到多个服


QQ客服