go-sqlite3是符合go database/sql接口的sqlite3驱动,需显式执行pragma启用外键和wal模式,连接字符串参数不保证生效,推荐搭配sqlx使用并设maxopenconns=1以避免锁冲突。

Iris 框架本身不内置 ORM 或数据库驱动,整合 SQLite 的关键不是“Iris 怎么配”,而是「Go 怎么安全、稳定地用 sqlite3」——Iris 只负责把 *sqlx.DB 或 *sql.DB 注入到 handler 中,真正的坑全在底层连接和 PRAGMA 设置上。
为什么别用 GORM + SQLite 在 Iris 里
GORM 默认按 PostgreSQL/MySQL 行为建模,而 SQLite 没有服务进程、不支持并发写、ALTER TABLE 不能 DROP COLUMN、时间函数行为不同(比如 datetime('now') 和 CURRENT_TIMESTAMP 在某些版本语义不一致)、外键默认关闭。你在 Iris 启动时调 AutoMigrate,大概率遇到:
-
no such column或near "DROP": syntax error—— 迁移失败 - 外键约束完全没生效,
FOREIGN KEY被 silently 忽略 - 事务提交后查不到刚插入的数据(因未设
PRAGMA journal_mode = WAL,读写冲突)
实际项目中,直接用 github.com/mattn/go-sqlite3 + github.com/jmoiron/sqlx 更可控。
连接初始化必须显式执行 PRAGMA
sql.Open("sqlite3", "./app.db") 只是打开句柄,不触发磁盘 I/O,也不启用外键或 WAL。这些设置对每个新连接都需重设(SQLite 没有“全局配置”),必须在 db.Ping() 前完成:
db, err := sqlx.Connect("sqlite3", "./app.db?_journal_mode=WAL&_synchronous=NORMAL")
if err != nil {
log.Fatal(err)
}
// 单独 exec PRAGMA,因为连接字符串里的 _journal_mode 不保证生效(尤其旧版驱动)
_, _ = db.Exec("PRAGMA foreign_keys = ON")
_, _ = db.Exec("PRAGMA journal_mode = WAL")
_, _ = db.Exec("PRAGMA synchronous = NORMAL")
if err := db.Ping(); err != nil {
log.Fatal("DB ping failed:", err)
}
注意:_journal_mode=WAL 在 DSN 里只是提示,实测某些 go-sqlite3 版本仍需 Exec 强制;synchronous = NORMAL 是平衡点,设 OFF 可能丢最后几条记录。
Iris 中如何传递和使用 DB 实例
Iris 的 app.Register 或自定义依赖注入可传 *sqlx.DB,但 handler 内部不要裸用 db.Query。应绑定请求上下文,便于超时控制和日志追踪:
- 注册全局 DB:在
main()初始化后调app.Register(db) - handler 中通过
c.Routine().Get("db").(*sqlx.DB)获取(推荐),或封装成中间件注入context.Context - 查询务必带 context:
db.GetContext(c.Request().Context(), &user, "SELECT * FROM users WHERE id = ?", id) - 避免在 handler 里调
db.Close()—— 它是全局复用的,关了整个服务就断库
SQLite 不支持连接池意义上的“多连接复用”,sqlx.DB 的 SetMaxOpenConns(1) 是合理选择,否则并发写会报 database is locked。
内存模式和测试场景的特殊处理
用 :memory: 做单元测试时,每个 sqlx.Connect 都是独立内存实例,无法跨 goroutine 共享数据。若需模拟真实行为,改用临时文件:
- 测试中用
tempfile.MkTemp("", "test-*.db")创建唯一路径,避免并行测试冲突 - 每次测试结束
os.Remove文件,不依赖defer db.Close()—— 内存 DB 关闭即销毁,但文件 DB 关闭不删文件 - 建表语句必须在每个测试 setup 阶段执行,SQLite 不会自动建表,
CREATE TABLE IF NOT EXISTS是必需的
最易被忽略的一点:SQLite 的 datetime 字段在 Go struct 中映射为 time.Time 时,必须在 DSN 加 _loc=Local 或手动解析,否则可能全变成 UTC 时间,且无错误提示。











