在golang中操作sqlite数据库,新手可通过以下步骤快速上手:1. 导入database/sql标准库和go-sqlite3驱动;2. 使用sql.open打开数据库连接并用defer确保关闭;3. 通过db.exec创建表;4. 使用db.prepare和stmt.exec插入数据;5. 利用db.query遍历查询结果;6. 使用预处理语句执行更新和删除操作。常见错误包括忽略错误处理、频繁打开关闭连接、sql注入风险、并发写入瓶颈及类型映射问题,建议启用wal模式提升并发性能,并使用预处理参数防止注入。为确保数据一致性,应使用事务处理,通过db.begin启动事务,tx.commit提交或tx.rollback回滚,所有操作均通过*sql.tx对象完成。此外,可借助golang-migrate实现schema迁移,或引入orm工具如gorm简化复杂业务逻辑。

在Golang中操作SQLite数据库,对于新手来说,其实比想象中要简单得多,核心就是利用好
database/sql
go-sqlite3

要开始用Go操作SQLite,首先得把
go-sqlite3
database/sql
database/sql
package main
import (
"database/sql"
"fmt"
"log"
_ "github.com/mattn/go-sqlite3" // 导入驱动,注意下划线,表示只导入包执行其init函数
)
func main() {
// 1. 打开数据库连接
// 如果文件不存在,会自动创建。这是一个很方便的特性。
db, err := sql.Open("sqlite3", "./test.db")
if err != nil {
log.Fatal(err)
}
// 养成好习惯,操作完数据库记得关闭连接。
// 这里用defer确保在函数退出时执行。
defer func() {
err := db.Close()
if err != nil {
log.Printf("关闭数据库连接失败: %v", err)
}
}()
// 2. 创建表(如果不存在)
// SQL语句写在这里,用`Exec`方法执行。
sqlStmt := `
CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
age INTEGER
);`
_, err = db.Exec(sqlStmt)
if err != nil {
log.Fatalf("创建表失败: %v", err)
}
fmt.Println("表 'users' 检查或创建成功。")
// 3. 插入数据
// 插入操作也用`Exec`。这里我推荐使用预处理语句(Prepared Statement),
// 这样可以有效防止SQL注入,而且对于重复插入性能更好。
stmt, err := db.Prepare("INSERT INTO users(name, age) VALUES(?, ?)")
if err != nil {
log.Fatalf("准备插入语句失败: %v", err)
}
defer func() {
err := stmt.Close()
if err != nil {
log.Printf("关闭预处理语句失败: %v", err)
}
}()
_, err = stmt.Exec("张三", 30)
if err != nil {
log.Fatalf("插入数据失败: %v", err)
}
fmt.Println("插入数据 '张三' 成功。")
_, err = stmt.Exec("李四", 25)
if err != nil {
log.Fatalf("插入数据 '李四' 失败: %v", err)
}
fmt.Println("插入数据 '李四' 成功。")
// 4. 查询数据
// 查询操作用`Query`。它会返回一个`*sql.Rows`对象,我们需要遍历它。
rows, err := db.Query("SELECT id, name, age FROM users")
if err != nil {
log.Fatalf("查询数据失败: %v", err)
}
defer func() {
err := rows.Close()
if err != nil {
log.Printf("关闭查询结果集失败: %v", err)
}
}()
fmt.Println("\n查询所有用户:")
for rows.Next() { // 遍历每一行
var id int
var name string
var age int
// 将列数据扫描到变量中
err = rows.Scan(&id, &name, &age)
if err != nil {
log.Fatalf("扫描行数据失败: %v", err)
}
fmt.Printf("ID: %d, 姓名: %s, 年龄: %d\n", id, name, age)
}
// 检查遍历过程中是否有错误发生
err = rows.Err()
if err != nil {
log.Fatalf("遍历结果集时发生错误: %v", err)
}
// 5. 更新数据
// 同样使用预处理语句,然后用`Exec`执行。
updateStmt, err := db.Prepare("UPDATE users SET age = ? WHERE name = ?")
if err != nil {
log.Fatalf("准备更新语句失败: %v", err)
}
defer func() {
err := updateStmt.Close()
if err != nil {
log.Printf("关闭更新预处理语句失败: %v", err)
}
}()
res, err := updateStmt.Exec(31, "张三")
if err != nil {
log.Fatalf("更新数据失败: %v", err)
}
rowsAffected, _ := res.RowsAffected() // 获取受影响的行数
fmt.Printf("\n更新 '张三' 的年龄为 31,影响行数: %d\n", rowsAffected)
// 6. 删除数据
// 删除操作也类似。
deleteStmt, err := db.Prepare("DELETE FROM users WHERE name = ?")
if err != nil {
log.Fatalf("准备删除语句失败: %v", err)
}
defer func() {
err := deleteStmt.Close()
if err != nil {
log.Printf("关闭删除预处理语句失败: %v", err)
}
}()
res, err = deleteStmt.Exec("李四")
if err != nil {
log.Fatalf("删除数据失败: %v", err)
}
rowsAffected, _ = res.RowsAffected()
fmt.Printf("删除 '李四',影响行数: %d\n", rowsAffected)
// 再次查询,看看删掉没
fmt.Println("\n再次查询所有用户 (验证删除):")
rows, err = db.Query("SELECT id, name, age FROM users")
if err != nil {
log.Fatalf("再次查询数据失败: %v", err)
}
defer func() {
err := rows.Close()
if err != nil {
log.Printf("关闭再次查询结果集失败: %v", err)
}
}()
for rows.Next() {
var id int
var name string
var age int
err = rows.Scan(&id, &name, &age)
if err != nil {
log.Fatalf("再次扫描行数据失败: %v", err)
}
fmt.Printf("ID: %d, 姓名: %s, 年龄: %d\n", id, name, age)
}
err = rows.Err()
if err != nil {
log.Fatalf("再次遍历结果集时发生错误: %v", err)
}
}在我看来,新手在使用Go和SQLite时,最容易犯的错误往往不是代码逻辑上的大问题,而是那些“小细节”:
立即学习“go语言免费学习笔记(深入)”;

sql.Open
db.Exec
db.Query
rows.Next
rows.Scan
err
log.Fatal
defer db.Close()
sql.Open
database/sql
sql.Open
Open
Close
Open
*sql.DB
"SELECT * FROM users WHERE name = '" + userName + "'"
db.Prepare()
stmt.Exec()
db.QueryRow()
db.Query()
go-sqlite3
database/sql
_journal=WAL
sql.Open("sqlite3", "./test.db?_journal=WAL")rows.Scan()
INTEGER
int
int64
TEXT
string
NULL
sql.NullString
sql.NullInt64
Scan
数据一致性,通常我们说的是事务(Transaction)的ACID特性:原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)、持久性(Durability)。简单来说,就是一系列数据库操作要么全部成功,要么全部失败,绝不会出现只完成了一部分的情况。在Go中操作SQLite,实现事务处理非常直观。
database/sql
db.Begin()
*sql.Tx
Exec
Query
*sql.Tx
*sql.DB
tx.Commit()
tx.Rollback()

一个常见的模式是,在
Begin()
defer tx.Rollback()
tx.Rollback()
nil
tx.Commit()
看个例子:
package main
import (
"database/sql"
"fmt"
"log"
_ "github.com/mattn/go-sqlite3"
)
func main() {
db, err := sql.Open("sqlite3", "./test_tx.db")
if err != nil {
log.Fatal(err)
}
defer func() {
err := db.Close()
if err != nil {
log.Printf("关闭数据库连接失败: %v", err)
}
}()
// 确保表存在
sqlStmt := `
CREATE TABLE IF NOT EXISTS accounts (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
balance INTEGER NOT NULL
);`
_, err = db.Exec(sqlStmt)
if err != nil {
log.Fatalf("创建表失败: %v", err)
}
fmt.Println("表 'accounts' 检查或创建成功。")
// 插入一些初始数据
_, err = db.Exec("INSERT INTO accounts(name, balance) VALUES('Alice', 100), ('Bob', 50)")
if err != nil {
log.Printf("插入初始数据失败 (可能已存在): %v", err)
}
// 模拟转账操作:从Alice转账20到Bob
fmt.Println("\n尝试进行转账操作 (Alice -> Bob, 20元)...")
err = transfer(db, "Alice", "Bob", 20)
if err != nil {
fmt.Printf("转账失败: %v\n", err)
} else {
fmt.Println("转账成功!")
}
// 模拟一次失败的转账(余额不足)
fmt.Println("\n尝试进行失败的转账操作 (Alice -> Bob, 200元)...")
err = transfer(db, "Alice", "Bob", 200)
if err != nil {
fmt.Printf("转账失败 (预期): %v\n", err)
} else {
fmt.Println("转账成功!")
}
// 查询最终余额
fmt.Println("\n查询最终账户余额:")
rows, err := db.Query("SELECT name, balance FROM accounts")
if err != nil {
log.Fatalf("查询余额失败: %v", err)
}
defer func() {
err := rows.Close()
if err != nil {
log.Printf("关闭查询结果集失败: %v", err)
}
}()
for rows.Next() {
var name string
var balance int
err = rows.Scan(&name, &balance)
if err != nil {
log.Fatalf("扫描余额数据失败: %v", err)
}
fmt.Printf("账户: %s, 余额: %d\n", name, balance)
}
err = rows.Err()
if err != nil {
log.Fatalf("遍历余额结果集时发生错误: %v", err)
}
}
// transfer 模拟转账函数
func transfer(db *sql.DB, fromUser, toUser string, amount int) error {
tx, err := db.Begin() // 开始事务
if err != nil {
return fmt.Errorf("开始事务失败: %w", err)
}
// 无论函数如何退出,都尝试回滚。
// 如果tx.Commit()成功执行,tx会变成nil,那么这个defer就不会执行了。
defer func() {
if tx != nil { // 只有当tx还没有被commit或rollback时才执行
err := tx.Rollback()
if err != nil && err != sql.ErrTxDone { // sql.ErrTxDone表示事务已完成
log.Printf("事务回滚失败: %v", err)
}
fmt.Println("事务已回滚。")
}
}()
// 1. 检查转出方余额
var fromBalance int
err = tx.QueryRow("SELECT balance FROM accounts WHERE name = ?", fromUser).Scan(&fromBalance)
if err != nil {
if err == sql.ErrNoRows {
return fmt.Errorf("转出账户 '%s' 不存在", fromUser)
}
return fmt.Errorf("查询转出方余额失败: %w", err)
}
if fromBalance < amount {
return fmt.Errorf("账户 '%s' 余额不足,当前: %d, 需转: %d", fromUser, fromBalance, amount)
}
// 2. 扣除转出方余额
_, err = tx.Exec("UPDATE accounts SET balance = balance - ? WHERE name = ?", amount, fromUser)
if err != nil {
return fmt.Errorf("扣除转出方余额失败: %w", err)
}
// 3. 增加转入方余额
_, err = tx.Exec("UPDATE accounts SET balance = balance + ? WHERE name = ?", amount, toUser)
if err != nil {
return fmt.Errorf("增加转入方余额失败: %w", err)
}
// 4. 提交事务
err = tx.Commit()
if err != nil {
return fmt.Errorf("提交事务失败: %w", err)
}
tx = nil // 成功提交后,将tx设为nil,避免defer中的回滚再次执行
return nil
}这个例子里,
transfer
当然,Go和SQLite的结合远不止增删改查这么简单,还有一些更高级的功能和实践,能让你的应用更健壮、更易维护:
golang-migrate/migrate
以上就是Golang新手怎样操作SQLite数据库 使用go-sqlite3驱动实践的详细内容,更多请关注php中文网其它相关文章!
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号