Go 语言中操作 SQLite

发布于:2025-06-29 ⋅ 阅读:(21) ⋅ 点赞:(0)

sqlite以其无需安装和配置:直接使用数据库文件,无需启动独立的数据库服务进程。

单文件存储:整个数据库(包括表、索引、数据等)存储在单个跨平台文件中,便于迁移和备份。

在应对的小型应用软件中.有着不可取代的地位.

sqlite使用 参考 10.1.SQLite-CSDN博客

10.2sql-CSDN博客 

go语言中使用sqlite

1. 推荐使用 go-sqlite3 驱动,它基于 C 语言的 SQLite 库实现:

go get github.com/mattn/go-sqlite3

2. 连接数据库

使用 Go 标准库 database/sql 结合驱动进行操作:

package main

import (
    "database/sql"
    "fmt"

    _ "github.com/mattn/go-sqlite3" // 导入但不直接使用
)

func main() {
    // 打开数据库连接(如果文件不存在会自动创建)
    db, err := sql.Open("sqlite3", "./test.db")
    if err != nil {
        panic(err)
    }
    defer db.Close() // 程序结束时关闭连接

    // 验证连接是否有效
    if err := db.Ping(); err != nil {
        panic(err)
    }

    fmt.Println("成功连接到SQLite数据库")
}

3. 创建表

使用 Exec 方法执行 SQL 语句:

// 创建表
func createTable(db *sql.DB) error {
    sqlStmt := `
    CREATE TABLE IF NOT EXISTS users (
        id INTEGER PRIMARY KEY AUTOINCREMENT,
        name TEXT NOT NULL,
        age INTEGER
    );
    `
    _, err := db.Exec(sqlStmt)
    return err
}

4. 插入数据

支持普通插入和预处理语句(防止 SQL 注入):

// 普通插入
func insertData(db *sql.DB, name string, age int) error {
    sqlStmt := fmt.Sprintf("INSERT INTO users (name, age) VALUES ('%s', %d)", name, age)
    _, err := db.Exec(sqlStmt)
    return err
}

// 预处理语句插入(推荐)
func insertDataWithPrepare(db *sql.DB, name string, age int) error {
    stmt, err := db.Prepare("INSERT INTO users (name, age) VALUES (?, ?)")
    if err != nil {
        return err
    }
    defer stmt.Close()

    _, err = stmt.Exec(name, age)
    return err
}

5. 查询数据

  • 单条记录:使用 QueryRow
  • 多条记录:使用 Query
// 查询单条记录
func querySingleUser(db *sql.DB, id int) (string, int, error) {
    var name string
    var age int
    err := db.QueryRow("SELECT name, age FROM users WHERE id = ?", id).Scan(&name, &age)
    if err != nil {
        return "", 0, err
    }
    return name, age, nil
}

// 查询多条记录
func queryAllUsers(db *sql.DB) ([]struct{ Name string; Age int }, error) {
    rows, err := db.Query("SELECT name, age FROM users")
    if err != nil {
        return nil, err
    }
    defer rows.Close()

    var users []struct{ Name string; Age int }
    for rows.Next() {
        var name string
        var age int
        if err := rows.Scan(&name, &age); err != nil {
            return nil, err
        }
        users = append(users, struct{ Name string; Age int }{name, age})
    }

    // 检查迭代过程中是否有错误
    if err := rows.Err(); err != nil {
        return nil, err
    }

    return users, nil
}

6. 更新和删除数据

// 更新数据
func updateUserAge(db *sql.DB, id, newAge int) error {
    stmt, err := db.Prepare("UPDATE users SET age = ? WHERE id = ?")
    if err != nil {
        return err
    }
    defer stmt.Close()

    _, err = stmt.Exec(newAge, id)
    return err
}

// 删除数据
func deleteUser(db *sql.DB, id int) error {
    stmt, err := db.Prepare("DELETE FROM users WHERE id = ?")
    if err != nil {
        return err
    }
    defer stmt.Close()

    _, err = stmt.Exec(id)
    return err
}

7. 事务处理

使用事务保证数据一致性:

func transferMoney(db *sql.DB, from, to int, amount float64) error {
    tx, err := db.Begin()
    if err != nil {
        return err
    }
    defer func() {
        if p := recover(); p != nil {
            tx.Rollback()
            panic(p) // 重新抛出异常
        } else if err != nil {
            tx.Rollback() // 发生错误时回滚
        } else {
            err = tx.Commit() // 提交事务
        }
    }()

    // 执行事务操作
    if _, err = tx.Exec("UPDATE accounts SET balance = balance - ? WHERE id = ?", amount, from); err != nil {
        return err
    }
    if _, err = tx.Exec("UPDATE accounts SET balance = balance + ? WHERE id = ?", amount, to); err != nil {
        return err
    }

    return nil
}

8. 启用 WAL 模式

在连接字符串中添加 _journal_mode=WAL 参数:

db, err := sql.Open("sqlite3", "./test.db?_journal_mode=WAL")

 SQLite 的 WAL(Write-Ahead Logging)模式是一种替代传统回滚日志(ROLLBACK JOURNAL)的事务机制,它能显著提升数据库的并发性能和写入吞吐量。

传统模式(ROLLBACK JOURNAL)
  • 写操作流程
    1. 将原始数据写入临时回滚日志(.db-journal)。
    2. 修改主数据库文件。
    3. 事务提交后删除回滚日志。
  • 缺点:写操作时需对整个数据库加独占锁,导致读写互斥,并发性能差。
WAL 模式
  • 写操作流程
    1. 将修改写入独立的 WAL 文件(.db-wal)。
    2. 事务提交时,只需要写入一个 4 字节的提交标记到 WAL 文件末尾。
    3. 读取操作直接从主数据库和 WAL 文件合并后的状态读取。
  • 优点:读写操作可以并发进行(读不阻塞写,写不阻塞读)。
读写并发
  • WAL 模式允许同时进行读和写操作,提升并发性能。
  • 限制:同一时间只能有一个写事务,但可以有多个并发读事务。

 

9. 错误处理

// 检查SQL执行结果
result, err := db.Exec("INSERT INTO ...")
if err != nil {
    log.Fatal(err)
}

// 获取插入ID
id, err := result.LastInsertId()
if err != nil {
    log.Fatal(err)
}

// 获取受影响行数
rowsAffected, err := result.RowsAffected()
if err != nil {
    log.Fatal(err)
}

完整示例

package main

import (
    "database/sql"
    "fmt"
    "log"

    _ "github.com/mattn/go-sqlite3"
)

func main() {
    // 连接数据库
    db, err := sql.Open("sqlite3", "./test.db")
    if err != nil {
        log.Fatal(err)
    }
    defer db.Close()

    // 创建表
    if err := createTable(db); err != nil {
        log.Fatal(err)
    }

    // 插入数据
    if err := insertDataWithPrepare(db, "Alice", 30); err != nil {
        log.Fatal(err)
    }

    // 查询数据
    users, err := queryAllUsers(db)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println("所有用户:", users)

    // 更新数据
    if err := updateUserAge(db, 1, 31); err != nil {
        log.Fatal(err)
    }

    // 删除数据
    if err := deleteUser(db, 1); err != nil {
        log.Fatal(err)
    }
}

注意事项

  1. 并发限制:SQLite 原生不支持多写操作,高并发场景需考虑锁机制或使用其他数据库。
  2. 文件权限:确保数据库文件所在目录可读写。
  3. SQL 注入:始终使用预处理语句(Prepare + Exec)避免 SQL 注入。


网站公告

今日签到

点亮在社区的每一天
去签到