如何使用Golang操作SQLite数据库_Golang database/sql基础操作示例

8次阅读

需先安装 mattn/go-sqlite3 驱动并下划线导入以注册,DSN 支持 WAL 模式等参数;QueryRow 用于单行查询,Query 用于多行遍历且须 Close;Exec 后可用 LastInsertId 和 RowsAffected 获取 ID 与影响行数;事务中 Rollback 必须 defer 且不可忽略错误。

如何使用Golang操作SQLite数据库_Golang database/sql基础操作示例

如何用 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 代码,需系统有 gccwindows 用户推荐用 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) 	} }

QueryRowQuery区别与选型场景

QueryRow 用于预期**至多一行结果**的语句(如 select ... LIMIT 1 或主键查询),它自动调用 Scan 并返回单行错误;Query 返回 *sql.Rows,适用于多行遍历,必须显式 rows.Close() 否则可能泄漏连接。

常见踩坑点:

立即学习go语言免费学习笔记(深入)”;

  • 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 ROWIDLastInsertId() 返回 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 不帮你做这个判断。

text=ZqhQzanResources