Go SQLite CURD 完整示例
Go 操作 SQLite 最常用库:github.com/mattn/go‑sqlite3,这是 cgo 实现的 SQLite 驱动,配合标准库 database/sql 完成全部 CURD。
注意:该库依赖 cgo,编译环境需要 GCC;如果不想用 cgo,可以选用纯 Go 实现:
modernc.org/sqlite,API 几乎完全一致。
1. 安装依赖
bash
# cgo版本
go get github.com/mattn/go-sqlite3
# 纯go无cgo版本(推荐跨平台编译)
go get modernc.org/sqlite使用
modernc.org/sqlite只需要把导入包改成_ "modernc.org/sqlite",其余代码不用修改。
2. 完整 CURD 示例代码
go
package main
import (
"database/sql"
"fmt"
"log"
_ "github.com/mattn/go-sqlite3"
)
// User 实体
type User struct {
ID int
Name string
Age int
}
func main() {
// 打开sqlite数据库,文件存在则打开,不存在自动创建
db, err := sql.Open("sqlite3", "./test.db")
if err != nil {
log.Fatalf("打开数据库失败: %v", err)
}
defer db.Close()
// 设置连接参数,sqlite单文件,一般设置1个连接
db.SetMaxOpenConns(1)
db.SetMaxIdleConns(1)
// 检查表是否存在,不存在创建表
err = createTable(db)
if err != nil {
log.Fatal(err)
}
// C 创建
uid, err := createUser(db, "张三", 22)
if err != nil {
log.Fatal(err)
}
fmt.Printf("新增用户id: %d\n", uid)
// R 查询单行
u, err := getUserByID(db, int(uid))
if err != nil {
log.Fatal(err)
}
fmt.Printf("查询单个:%+v\n", u)
// R 查询多行
users, err := listUser(db)
if err != nil {
log.Fatal(err)
}
fmt.Printf("全部用户:%+v\n", users)
// U 更新
affect, err := updateUser(db, int(uid), "张三改名字", 25)
if err != nil {
log.Fatal(err)
}
fmt.Printf("更新影响行数:%d\n", affect)
// D 删除
delCnt, err := deleteUser(db, int(uid))
if err != nil {
log.Fatal(err)
}
fmt.Printf("删除行数:%d\n", delCnt)
}
// 创建表
func createTable(db *sql.DB) error {
sqlStr := `
CREATE TABLE IF NOT EXISTS user (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
age INTEGER
);
`
_, err := db.Exec(sqlStr)
return err
}
// C 新增
func createUser(db *sql.DB, name string, age int) (int64, error) {
stmt, err := db.Prepare(`INSERT INTO user(name,age) values (?,?)`)
if err != nil {
return 0, err
}
defer stmt.Close()
res, err := stmt.Exec(name, age)
if err != nil {
return 0, err
}
// 获取自增id
return res.LastInsertId()
}
// R 根据id查询单条
func getUserByID(db *sql.DB, id int) (*User, error) {
row := db.QueryRow(`SELECT id,name,age FROM user WHERE id = ?`, id)
var u User
err := row.Scan(&u.ID, &u.Name, &u.Age)
if err != nil {
// 查询不到记录
if err == sql.ErrNoRows {
return nil, nil
}
return nil, err
}
return &u, nil
}
// R 查询全部列表
func listUser(db *sql.DB) ([]User, error) {
rows, err := db.Query(`SELECT id,name,age FROM user`)
if err != nil {
return nil, err
}
defer rows.Close()
var list []User
for rows.Next() {
var u User
if err := rows.Scan(&u.ID, &u.Name, &u.Age); err != nil {
return nil, err
}
list = append(list, u)
}
// 遍历结束检查错误
if err := rows.Err(); err != nil {
return nil, err
}
return list, nil
}
// U 更新
func updateUser(db *sql.DB, id int, name string, age int) (int64, error) {
res, err := db.Exec(`UPDATE user SET name=?, age=? WHERE id=?`, name, age, id)
if err != nil {
return 0, err
}
return res.RowsAffected()
}
// D 删除
func deleteUser(db *sql.DB, id int) (int64, error) {
res, err := db.Exec(`DELETE FROM user WHERE id=?`, id)
if err != nil {
return 0, err
}
return res.RowsAffected()
}核心API说明
- sql.Open:只是建立句柄,不会立刻连接数据库,真正验证连通可以执行
db.Ping()
go
if err := db.Ping(); err != nil {
log.Fatal(err)
}Exec:执行无返回结果SQL:INSERT / UPDATE / DELETE / DDL,返回Result,可以拿到LastInsertId()、RowsAffected()Query:返回多行结果集,必须defer rows.Close(),否则会泄露连接QueryRow:只取一行,没有返回sql.ErrNoRows错误Prepare:预编译语句,防止SQL注入,重复执行时性能更好,用完defer stmt.Close()- SQLite占位符统一用
?,不要用%s字符串拼接SQL,避免注入。
事务示例
go
func txDemo(db *sql.DB) error {
tx, err := db.Begin()
if err != nil {
return err
}
defer func() {
if r := recover(); r != nil {
_ = tx.Rollback()
}
}()
_, err = tx.Exec(`INSERT INTO user(name,age) values(?,?)`, "A", 18)
if err != nil {
_ = tx.Rollback()
return err
}
_, err = tx.Exec(`INSERT INTO user(name,age) values(?,?)`, "B", 19)
if err != nil {
_ = tx.Rollback()
return err
}
return tx.Commit()
}modernc.org/sqlite 纯Go版本差异
- 不需要gcc,交叉编译友好,适合打包部署
- 导入包改为
_ "modernc.org/sqlite",业务代码完全一样 - dsn可以开启WAL模式(提升并发性能)
go
db, err := sql.Open("sqlite", "file:test.db?_journal=WAL&_timeout=5000")SQLite 常用优化点(Go)
- 开启WAL模式,大幅提升读写并发
sql
PRAGMA journal_mode=WAL;
PRAGMA synchronous=NORMAL;- sqlite文件数据库不要设置大量连接池,
SetMaxOpenConns=1是最佳实践,SQLite文件锁不支持多写并发。 - 批量插入优先使用事务包裹,不要单条提交。