深入探究 Go 语言中使用 SQLite 数据库

引言

在软件开发中,数据库是管理和存储数据的关键组件。SQLite 作为一款轻量级的嵌入式数据库,因其零配置、高性能和易于集成等特性,成为众多小型项目和嵌入式系统的理想选择。而 Go 语言以其高效、简洁的特点,为操作 SQLite 数据库提供了强大的支持。本文将结合实际代码,深入介绍如何在 Go 语言中使用 SQLite 数据库,包括数据库和表的创建、增删改查操作以及操作的优化。

环境准备

在开始使用 Go 语言操作 SQLite 数据库之前,我们需要安装必要的库。可以使用以下命令安装github.com/mattn/go-sqlite3库:

复制代码
go get github.com/mattn/go-sqlite3

创建数据库和表

创建数据库

在 Go 语言中,使用database/sql包结合github.com/mattn/go-sqlite3驱动来操作 SQLite 数据库。通过sql.Open函数可以打开或创建一个 SQLite 数据库文件。以下是一个简单的示例:

Go 复制代码
package main

import (
    "database/sql"
    "fmt"
    "path/filepath"

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

const defDBPath = "./data"

func initContDB() error {
    dbFile := filepath.Join(defDBPath, "test.db")
    var err error
    gDB, err := sql.Open("sqlite3", dbFile)
    if err != nil {
        fmt.Println("open test db ", err)
        return err
    }
    fmt.Println("open test db ok")
    _, err = gDB.Query("SELECT COUNT(*) FROM sqlite_master")
    if err != nil {
        fmt.Println("test db query ", err)
        return err
    }
    return nil
}

在上述代码中,sql.Open函数接受两个参数:驱动名(这里是sqlite3)和数据库文件的路径。如果文件不存在,SQLite 会自动创建该文件。

创建表

在创建数据库之后,我们需要创建表来存储数据。可以使用Exec方法执行 SQL 语句来创建表。为了避免重复创建表,我们可以先检查表是否已经存在。以下是创建表的示例代码:

复制代码
func existTable(db *sql.DB, tbl string) bool {
    stmt, err := db.Prepare("SELECT COUNT(*) FROM sqlite_master where tbl_name=?")
    if err != nil {
        fmt.Println(err)
        return false
    }
    defer stmt.Close()
    row := stmt.QueryRow(tbl)
    if row == nil {
        return false
    }
    var val int
    row.Scan(&val)
    if val == 0 {
        return false
    }
    return true
}

func createTable() {
    if existTable(gDB, "t_cont_info") {
        return
    }
    tbl := `CREATE TABLE [t_cont_info](
              [name] VARCHAR PRIMARY KEY NOT NULL UNIQUE, 
              [ver] VARCHAR, 
              [image] VARCHAR NOT NULL, 
              [cmd] INT(2) NOT NULL, 
              [opt_cmd] INT(2) NOT NULL, 
              [cpus] INT(2) NOT NULL, 
              [cpu_threshold] INT(4) NOT NULL, 
              [memory] INT(4) NOT NULL, 
              [mem_threshold] INT(4) NOT NULL, 
              [store_cap] INT(4) NOT NULL, 
              [store_threshold] INT(4) NOT NULL, 
              [ip] VARCHAR, 
              [create_time] VARCHAR, 
              [start_time] VARCHAR, 
              [runtimes] BIGINT, 
              [backup_path] VARCHAR,
              [opt_time] VARCHAR);
            `
    _, err := gDB.Exec(tbl)
    if err != nil {
        fmt.Println(err)
        return
    }
    fmt.Println("creatTable t_cont_info")
}

在上述代码中,existTable函数用于检查指定的表是否存在。createTable函数根据检查结果决定是否创建t_cont_info表。

增删改查操作

插入数据

插入数据可以使用PrepareExec方法。以下是插入的示例代码:

复制代码
func insertWarnMsg(name, msg string) error {
    stmt, err := gWarnDB.Prepare(`INSERT INTO t_cont_info
                ([name], [msg], [opt_time]) VALUES (?,?,?)`)
    if err != nil {
        fmt.Println(err)
        return err
    }
    _, err = stmt.Exec(name, msg, getLocalTime())
    if err != nil {
        stmt.Close()
        fmt.Println(err)
        return err
    }
    stmt.Close()

    checkWarnLimit(name)
    return nil
}

在上述代码中,Prepare方法用于准备 SQL 语句,Exec方法用于执行插入操作。

更新数据

更新数据的操作与插入数据类似,同样使用PrepareExec方法。以下是更新的示例代码:

复制代码
func updContInfo(ct ContItem) {
    smt, err := gDB.Prepare("SELECT [name] FROM t_cont_info WHERE name=?")
    if err != nil {
        fmt.Println(err)
        return
    }
    defer smt.Close()
    upd := false
    row := smt.QueryRow(ct.Name)
    if row != nil {
        var name string
        err = row.Scan(&name)
        if err != nil {

        } else if name == ct.Name {
            upd = true
        }
    }
    if upd {
        sqlCmd := `UPDATE t_cont_info SET [ver]=?, [image]=?, [cmd]=?, [opt_cmd]=?, [cpus]=?, [cpu_threshold]=?, 
            [memory]=?, [mem_threshold]=?, [store_cap]=?, [store_threshold]=?, [ip]=?, [create_time]=?, 
            [start_time]=?, [runtimes]=?, [backup_path]=?, [opt_time]=? WHERE [name]=?`
        stmt, err := gDB.Prepare(sqlCmd)
        if err != nil {
            fmt.Println(err, ", sql: ", sqlCmd)
            return
        }
        defer stmt.Close()
        _, err = stmt.Exec(ct.Ver, ct.Img, ct.Cmd, ct.OptCmd, ct.CPUs, ct.CPUThreshold,
            ct.Memory, ct.MemThreshold, ct.StoreCap, ct.StoreThreshold, ct.IP, ct.CreateTime,
            ct.StartTime, ct.RunTimes, ct.BackupPath, getLocalTime(), ct.Name)
        if err != nil {
            fmt.Println(err)
            return
        }
    } else {
        stmt, err := gDB.Prepare(`INSERT INTO t_cont_info
                ([name] , [ver], [image], [cmd], [opt_cmd], [cpus], [cpu_threshold], 
                [memory], [mem_threshold], [store_cap], [store_threshold], [ip], [create_time], 
                [start_time], [runtimes], [backup_path], [opt_time]
                ) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)`)
        if err != nil {
            fmt.Println(err)
            return
        }
        defer stmt.Close()
        _, err = stmt.Exec(ct.Name, ct.Ver, ct.Img, ct.Cmd, ct.OptCmd, ct.CPUs, ct.CPUThreshold,
            ct.Memory, ct.MemThreshold, ct.StoreCap, ct.StoreThreshold, ct.IP, ct.CreateTime,
            ct.StartTime, ct.RunTimes, ct.BackupPath, getLocalTime())
        if err != nil {
            fmt.Println(err)
            return
        }
    }
}

在上述代码中,首先检查数据是否存在,如果存在则更新数据,否则插入新数据。

删除数据

删除数据可以使用PrepareExec方法执行DELETE语句。以下是删除的示例代码:

复制代码
func deleteContItem(name string) {
    del := func(tbl, name string) {
        stmt, err := gDB.Prepare(fmt.Sprintf("DELETE FROM %s WHERE [name]=?", tbl))
        if err != nil {
            fmt.Println(err)
            return
        }
        defer stmt.Close()
        _, err = stmt.Exec(name)
        if err != nil {
            fmt.Println(err)
            return
        }
    }

    del("t_cont_info", name)
    del("t_cont_res_map", name)
    fmt.Println("deleteContItem: ", name)
}

在上述代码中,定义了一个匿名函数del来执行删除操作,分别删除t_cont_infot_cont_res_map表中指定名称的数据。

查询数据

查询数据可以使用Query方法执行SELECT语句,并使用Scan方法将结果扫描到变量中。以下是查询所有数据的示例代码:

复制代码
func loadFromDB() (lst []ContItem, err error) {
    rows, err := gDB.Query(`SELECT [name] , [ver], [image], [cmd], [opt_cmd], [cpus], 
                    [cpu_threshold], [memory], [mem_threshold], [store_cap], [store_threshold], 
                    [ip], [create_time], [start_time], [runtimes], [backup_path] FROM t_cont_info`)
    if err != nil {
        fmt.Println(err)
        return
    }
    defer rows.Close()
    for rows.Next() {
        var ct ContItem
        rows.Scan(&ct.Name, &ct.Ver, &ct.Img, &ct.Cmd, &ct.OptCmd, &ct.CPUs, &ct.CPUThreshold,
            &ct.Memory, &ct.MemThreshold, &ct.StoreCap, &ct.StoreThreshold, &ct.IP,
            &ct.CreateTime, &ct.StartTime, &ct.RunTimes, &ct.BackupPath)
        ct.DevMapList, _ = loadContResMap(ct.Name, CONT_RES_DEV)
        ct.VolMapList, _ = loadContResMap(ct.Name, CONT_RES_VOL)
        ct.PortMapList, _ = loadContResMap(ct.Name, CONT_RES_PORT)
        lst = append(lst, ct)
        fmt.Println("loadFromDB: ", ct)
    }
    return
}

在上述代码中,Query方法返回一个Rows对象,通过Next方法遍历结果集,并使用Scan方法将数据存储到ContItem结构体中。

操作优化

批量操作

在进行大量数据的插入或更新操作时,可以使用事务来提高性能。事务可以将多个操作合并为一个原子操作,减少数据库的开销。以下是一个使用事务进行批量插入的示例代码:

复制代码
func batchInsert(data []interface{}) error {
    tx, err := gDB.Begin()
    if err != nil {
        return err
    }
    stmt, err := tx.Prepare("INSERT INTO table_name (column1, column2) VALUES (?,?)")
    if err != nil {
        tx.Rollback()
        return err
    }
    defer stmt.Close()
    for _, item := range data {
        _, err = stmt.Exec(item.Value1, item.Value2)
        if err != nil {
            tx.Rollback()
            return err
        }
    }
    return tx.Commit()
}

在上述代码中,Begin方法开始一个事务,Prepare方法准备 SQL 语句,Exec方法执行插入操作,最后使用Commit方法提交事务。如果发生错误,使用Rollback方法回滚事务。

索引优化

为经常用于查询条件的列创建索引可以提高查询性能。例如,在t_cont_info表中,如果经常根据name列进行查询,可以为name列创建索引:

复制代码
func createIndex() {
    _, err := gDB.Exec("CREATE INDEX idx_name ON t_cont_info (name)")
    if err != nil {
        fmt.Println(err)
        return
    }
    fmt.Println("create index idx_name")
}

在上述代码中,使用CREATE INDEX语句为name列创建索引。

总结

本文深入介绍了如何在 Go 语言中使用 SQLite 数据库,包括数据库和表的创建、增删改查操作以及操作的优化。通过使用database/sql包和github.com/mattn/go-sqlite3驱动,我们可以方便地操作 SQLite 数据库。在实际开发中,根据具体需求选择合适的操作方法和优化策略,可以提高程序的性能和稳定性。

相关推荐
沃尔威武6 小时前
数据库 Sinks(.net8)
数据库·.net·webview
Dreamboat¿7 小时前
SQL 注入漏洞
数据库·sql
Dontla8 小时前
go语言Windows安装教程(安装go安装Golang安装)(GOPATH、Go Modules)
开发语言·windows·golang
铁东博客8 小时前
Go实现周易大衍筮法三变取爻
开发语言·后端·golang
曹牧8 小时前
Oracle数据库中,将JSON字符串转换为多行数据
数据库·oracle·json
被摘下的星星8 小时前
MySQL count()函数的用法
数据库·mysql
末央&8 小时前
【天机论坛】项目环境搭建和数据库设计
java·数据库
徒 花9 小时前
数据库知识复习07
数据库·作业
素玥9 小时前
实训5 python连接mysql数据库
数据库·python·mysql
jnrjian9 小时前
text index 查看index column index定义 index 刷新频率 index视图
数据库·oracle