欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Golang對(duì)sqlite3數(shù)據(jù)庫(kù)進(jìn)行操作實(shí)踐記錄

 更新時(shí)間:2024年03月13日 09:27:22   作者:李遲  
sqlite是嵌入式關(guān)系型數(shù)據(jù)庫(kù)引擎,官方描述為自包含的、無(wú)服務(wù)的、零配置并支持事務(wù)的關(guān)系型數(shù)據(jù)庫(kù)引擎,下面這篇文章主要給大家介紹了關(guān)于Golang對(duì)sqlite3數(shù)據(jù)庫(kù)進(jìn)行操作的相關(guān)資料,需要的朋友可以參考下

本文使用 Golang 對(duì) sqlite3 數(shù)據(jù)庫(kù)進(jìn)行操作。

概述

Golang 操作數(shù)據(jù)庫(kù)有統(tǒng)一的接口,當(dāng)然也有xorm這樣的庫(kù),筆者接觸的項(xiàng)目不大,對(duì)sql自由組裝有要求,同時(shí)也會(huì)將這些sql用于數(shù)據(jù)庫(kù)客戶端查詢,因此偏向于使用原生的sql。

為方便起見,本文只針對(duì)sqlite進(jìn)行連接、讀寫、事務(wù)的測(cè)試。理論上可以擴(kuò)展到其它數(shù)據(jù)庫(kù)的操作。

技術(shù)小結(jié)

  • 引入的包有"database/sql"、_ "github.com/mattn/go-sqlite3"
  • 使用sql.Open打開數(shù)據(jù)庫(kù),對(duì)于sqlite3,不存在目標(biāo)文件時(shí),會(huì)自創(chuàng)并使用。
  • 事務(wù)相關(guān)接口有:開始SQLDB.Begin()、提交tx.Commit()、回滾tx.Rollback()、結(jié)束SQLDB.Close()。

設(shè)計(jì)

為讓測(cè)試代碼接近業(yè)務(wù)邏輯,設(shè)計(jì)場(chǎng)景如下:

  • 設(shè)2個(gè)數(shù)據(jù)表:一為版本號(hào)表,一為信息明細(xì)表。
  • 版本號(hào)更新了(如通過(guò)http下載數(shù)據(jù),數(shù)據(jù)中有版本號(hào)),才更新明細(xì)表。程序通過(guò)讀取數(shù)據(jù)庫(kù)表的版本號(hào)進(jìn)行判斷。
  • 允許上述數(shù)據(jù)表為空或不存在,由于sqlite3是基于文件的,也允許sqlite文件不存在。
  • 同時(shí)寫上述2個(gè)數(shù)據(jù)表,同時(shí)成功了方認(rèn)為成功,因此使用到事務(wù)機(jī)制。

源碼分析

完整代碼見文后,本節(jié)按實(shí)現(xiàn)功能列出要點(diǎn)。

連接數(shù)據(jù)庫(kù)

func CreateSqlite3(dbname string, create bool) (sqldb *sql.DB, err error) {
    if create == false && !IsExist(dbname) {
        return nil, errors.New("open database failed: " + dbname + " not found")
    }
    sqldb, err = sql.Open("sqlite3", dbname)
    if err != nil {
        return nil, errors.New("open database failed: " + err.Error())
    }
    err = sqldb.Ping()
    if err != nil {
        return nil, errors.New("connect database failed: " + err.Error())
    }
    fmt.Println("connect to ", dbname, "ok")

    return
}

讀取版本號(hào)

讀取版本號(hào),如果不存在,則創(chuàng)建對(duì)應(yīng)的表。

func readOrCreateDBTable(sqldb *sql.DB) (version, updateTime string) {
    needCreate := false
    sqlstr := fmt.Sprintf(`select version, updateTime from %v order by version desc limit 1`,
        tableVersion)
    fmt.Printf("run sql: [%v]\n", sqlstr)
    results, err := sqldb.Query(sqlstr)
    if err != nil {
        if strings.Contains(err.Error(), "no such table") {
            needCreate = true
        } else {
            fmt.Println("query error: ", err)
            return
        }
    }

    if !needCreate {
        for results.Next() {
            var item1, item2 sql.NullString
            err := results.Scan(&item1, &item2)
            if err != nil {
                fmt.Println("scan error: ", err)
                break
            }
            if !item1.Valid || !item2.Valid {
                continue
            }
            version = item1.String
            updateTime = item2.String
        }

        defer results.Close()
    } else {
        fmt.Println("not found table, will create it.")
        for _, item := range sqlarr {
            _, err := sqldb.Exec(item)
            if err != nil {
                fmt.Printf("Exec sql failed: [%v] [%v] \n", err, item)
            }
        }
    }

    return
}

以事務(wù)方式入庫(kù)

// 入庫(kù)2個(gè)表,以事務(wù)方式
func insertDBBatch(gxList []InfoList_t, version string) (err error) {
    SQLDB, err := CreateSqlite3(dbServer, false)
    if err != nil {
        // fmt.Println(err.Error())
        return err
    }

    var tx *sql.Tx
    tx, err = SQLDB.Begin()
    if err != nil {
        err = errors.New("begin sql error: " + err.Error())
        return err
    }

    defer func() {
        if err != nil {
            err = errors.New("exec sql failed rollback: " + err.Error())
            tx.Rollback()
        } else {
            err = nil
            tx.Commit()
        }
        // 延時(shí)一會(huì),關(guān)閉
        Sleep(1000)
        SQLDB.Close()
    }()

    err = insertDBVersion(tx, version)
    if err != nil {
        return
    }

    err = insertDBDetail(tx, gxList, version)
    if err != nil {
        return
    }

    return
}

函數(shù)開始時(shí),先調(diào)用SQLDB.Begin()開始事務(wù),分別調(diào)用insertDBVersioninsertDBDetail入庫(kù),只有2者同時(shí)成功,才調(diào)用tx.Commit()提交事務(wù),否則調(diào)用tx.Rollback()回滾。提交事務(wù)或回滾,通過(guò)Golang的defer機(jī)制實(shí)現(xiàn),邏輯較清晰。

測(cè)試

測(cè)試日志如下:

go test -v -run TestSqlite

沒有數(shù)據(jù)庫(kù)文件
test of sqlte3...
connect to  foobar.db3 ok
run sql:
select version, updateTime from myVersion order by version desc limit 1
not found table, will create it.
got db version [] update time []
connect to  foobar.db3 ok
insert db version [] at: [2023-12-02 10:42:18]
insert result:  <nil>
--- PASS: TestSqlite (1.04s)
PASS

已有數(shù)據(jù)但版本較新
test of sqlte3...
connect to  foobar.db3 ok
run sql: [select version, updateTime from myVersion order by version desc limit 1]
got db version [20231202] update time [2023-12-02T10:48:20Z]
connect to  foobar.db3 ok
insert db version [20231203] at: [2023-12-02 10:48:47]
insert result:  <nil>
--- PASS: TestSqlite (1.03s)
PASS

完整代碼

package test

import (
    "database/sql"
    "errors"
    "fmt"
    "os"
    "strings"
    "testing"
    "time"
    "webdemo/pkg/com"

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

var (
    // 數(shù)據(jù)庫(kù)文件名及表名
    dbServer     string = "foobar.db3"
    tableVersion string = "myVersion"
    tableList    string = "myList"
)

// 信息表 結(jié)構(gòu)體可對(duì)于json風(fēng)格數(shù)據(jù)傳輸解析
type InfoList_t struct {
    Id         int    `json:"-"`
    Version    string `json:"-"`
    Name       string `json:"-"`
    City       string `json:"-"`
    UpdateTime string `json:"-"`
}

var sqlarr []string = []string{
    // 版本號(hào)
    `CREATE TABLE "myVersion" (
        "version" VARCHAR(20) NOT NULL,
        "updateTime" datetime DEFAULT "",
        PRIMARY KEY ("version")
    );`,

    // 信息表
    `CREATE TABLE "myList" (
        "id" int NOT NULL,
        "version" VARCHAR(20) NOT NULL,
        "name" VARCHAR(20) NOT NULL,
        "city" VARCHAR(20) NOT NULL,
        "updateTime" datetime DEFAULT "",
        PRIMARY KEY ("id")
    );`,
}

func IsExist(path string) bool {
    _, err := os.Stat(path)
    return err == nil || os.IsExist(err)
}

func Sleep(ms int) {
    time.Sleep(time.Duration(ms) * time.Millisecond)
}

func CreateSqlite3(dbname string, create bool) (sqldb *sql.DB, err error) {
    if create == false && !IsExist(dbname) {
        return nil, errors.New("open database failed: " + dbname + " not found")
    }
    sqldb, err = sql.Open("sqlite3", dbname)
    if err != nil {
        return nil, errors.New("open database failed: " + err.Error())
    }
    err = sqldb.Ping()
    if err != nil {
        return nil, errors.New("connect database failed: " + err.Error())
    }
    fmt.Println("connect to ", dbname, "ok")

    return
}

func readOrCreateDBTable(sqldb *sql.DB) (version, updateTime string) {
    needCreate := false
    sqlstr := fmt.Sprintf(`select version, updateTime from %v order by version desc limit 1`,
        tableVersion)
    fmt.Printf("run sql: [%v]\n", sqlstr)
    results, err := sqldb.Query(sqlstr)
    if err != nil {
        if strings.Contains(err.Error(), "no such table") {
            needCreate = true
        } else {
            fmt.Println("query error: ", err)
            return
        }
    }

    if !needCreate {
        for results.Next() {
            var item1, item2 sql.NullString
            err := results.Scan(&item1, &item2)
            if err != nil {
                fmt.Println("scan error: ", err)
                break
            }
            if !item1.Valid || !item2.Valid {
                continue
            }
            version = item1.String
            updateTime = item2.String
        }

        defer results.Close()
    } else {
        fmt.Println("not found table, will create it.")
        for _, item := range sqlarr {
            _, err := sqldb.Exec(item)
            if err != nil {
                fmt.Printf("Exec sql failed: [%v] [%v] \n", err, item)
            }
        }
    }

    return
}

func insertDBDetail(tx *sql.Tx, gxList []InfoList_t, version string) (err error) {
    tablename := tableList
    sqlstr := fmt.Sprintf(`DELETE FROM %v`, tablename)
    stmt, err := tx.Prepare(sqlstr)
    if err != nil {
        err = errors.New("prepare for [" + sqlstr + "] failed: " + err.Error())
        return
    }
    _, err = stmt.Exec()
    if err != nil {
        err = errors.New("delete " + tablename + "failed: " + err.Error())
        return
    }

    sqlstr = fmt.Sprintf(`INSERT OR REPLACE INTO %v 
(id, version, name, city, updateTime) 
VALUES (?, ?, ?, ?, ?)`,
        tablename)
    stmt, _ = tx.Prepare(sqlstr)
    for _, item := range gxList {
        // item.Id = idx
        item.Version = version
        item.UpdateTime = com.GetNowDateTime("YYYY-MM-DD HH:mm:ss")
        _, err = stmt.Exec(item.Id, item.Version, item.Name, item.City, item.UpdateTime)
        if err != nil {
            err = errors.New("insert " + tablename + "failed: " + err.Error())
            return
        }
    }

    return
    // debug 制作bug
    // TODO 制作鎖住,制作語(yǔ)法錯(cuò)誤
    err = errors.New("database is locked")

    return
}

func insertDBVersion(tx *sql.Tx, version string) (err error) {
    tablename := tableVersion
    sqlstr := fmt.Sprintf(`DELETE FROM %v`, tablename)
    stmt, err := tx.Prepare(sqlstr)
    if err != nil {
        err = errors.New("prepare for [" + sqlstr + "] failed: " + err.Error())
        return
    }
    _, err = stmt.Exec()
    if err != nil {
        err = errors.New("delete " + tablename + " failed: " + err.Error())
        return
    }

    sqlstr = fmt.Sprintf(`INSERT OR REPLACE INTO %v (version, updateTime) VALUES (?, ?)`, tablename)
    stmt, err = tx.Prepare(sqlstr)
    if err != nil {
        err = errors.New("prepare for [" + sqlstr + "] failed: " + err.Error())
        return
    }
    updateTime := com.GetNowDateTime("YYYY-MM-DD HH:mm:ss")
    fmt.Printf("insert db version [%v] at: [%v]\n", version, updateTime)
    _, err = stmt.Exec(version, updateTime)
    if err != nil {
        err = errors.New("insert " + tablename + "failed: " + err.Error())
        return
    }

    return
}

// 入庫(kù)2個(gè)表,以事務(wù)方式
func insertDBBatch(gxList []InfoList_t, version string) (err error) {
    SQLDB, err := CreateSqlite3(dbServer, false)
    if err != nil {
        // fmt.Println(err.Error())
        return err
    }

    var tx *sql.Tx
    tx, err = SQLDB.Begin()
    if err != nil {
        err = errors.New("begin sql error: " + err.Error())
        return err
    }

    defer func() {
        if err != nil {
            err = errors.New("exec sql failed rollback: " + err.Error())
            tx.Rollback()
        } else {
            err = nil
            tx.Commit()
        }
        // 延時(shí)一會(huì),關(guān)閉
        Sleep(1000)
        SQLDB.Close()
    }()

    err = insertDBVersion(tx, version)
    if err != nil {
        return
    }

    err = insertDBDetail(tx, gxList, version)
    if err != nil {
        return
    }

    return
}

//
func makeData() (gxList []InfoList_t) {
    var tmp InfoList_t
    tmp.Id = 100
    tmp.Version = "100"
    tmp.Name = "latelee"
    tmp.City = "梧州"
    gxList = append(gxList, tmp)

    tmp = InfoList_t{}
    tmp.Id = 250
    tmp.Version = "250"
    tmp.Name = "latelee"
    tmp.City = "岑溪"
    gxList = append(gxList, tmp)

    return
}

// 讀取基礎(chǔ)信息,嘗試創(chuàng)建表
func readDBVersion() (version, datetime string) {
    SQLDB, err := CreateSqlite3(dbServer, true)
    if err != nil {
        fmt.Println(err.Error())
        return
    }
    version, datetime = readOrCreateDBTable(SQLDB)
    SQLDB.Close()

    return
}
func TestSqlite(t *testing.T) {
    fmt.Println("test of sqlte3...")

    // 1 嘗試獲取數(shù)據(jù)表的版本號(hào)(可能為空)
    version, datetime := readDBVersion()
    fmt.Printf("got db version [%v] update time [%v]\n", version, datetime)

    // 2 模擬業(yè)務(wù):自定義版本號(hào),較新時(shí),才入庫(kù)
    myVer := "20231202"
    if myVer > version {
        data := makeData()
        err := insertDBBatch(data, myVer)
        fmt.Println("insert result: ", err)
    } else {
        fmt.Println("db is newest, do nothing")
    }

}

總結(jié) 

到此這篇關(guān)于Golang對(duì)sqlite3數(shù)據(jù)庫(kù)進(jìn)行操作的文章就介紹到這了,更多相關(guān)Golang使用sqlite內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Go語(yǔ)言版本管理module以及go.sum詳解

    Go語(yǔ)言版本管理module以及go.sum詳解

    本文介紹了Go語(yǔ)言版本管理go module以及go.sum詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2024-08-08
  • golang中的struct操作

    golang中的struct操作

    結(jié)構(gòu)體是一種聚合的數(shù)據(jù)類型,是由零個(gè)或多個(gè)任意類型的值聚合成的實(shí)體,每個(gè)值稱為結(jié)構(gòu)體的成員。下面介紹下golang中的struct,感興趣的朋友一起看看吧
    2021-11-11
  • 詳解Go hash算法的支持

    詳解Go hash算法的支持

    這篇文章主要介紹了詳解Go hash算法的支持,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2018-09-09
  • Go Plugins插件的實(shí)現(xiàn)方式

    Go Plugins插件的實(shí)現(xiàn)方式

    目前 Plugins 僅在 Linux、FreeBSD 和 macOS 上受支持,且只支持 golang 調(diào)用,今天通過(guò)本文給大家介紹Go Plugins插件的實(shí)現(xiàn)方式,感興趣的朋友一起看看吧
    2021-08-08
  • GORM中Model和Table的區(qū)別及使用

    GORM中Model和Table的區(qū)別及使用

    Model?和Table是兩種與數(shù)據(jù)庫(kù)表交互的核心方法,但它們的用途和行為存在著差異,本文主要介紹了GORM中Model和Table的區(qū)別及使用,具有一定的參考價(jià)值,感興趣的可以了解一下
    2025-03-03
  • 使用Go module和GoLand初始化一個(gè)Go項(xiàng)目的方法

    使用Go module和GoLand初始化一個(gè)Go項(xiàng)目的方法

    這篇文章主要介紹了使用Go module和GoLand初始化一個(gè)Go項(xiàng)目,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-12-12
  • go xorm存庫(kù)處理null值問(wèn)題

    go xorm存庫(kù)處理null值問(wèn)題

    這篇文章主要介紹了go xorm存庫(kù)處理null值問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-12-12
  • GO項(xiàng)目配置與使用的方法步驟

    GO項(xiàng)目配置與使用的方法步驟

    本文主要介紹了GO項(xiàng)目配置與使用的方法步驟,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧<BR>
    2022-06-06
  • Golang flag包的具體使用

    Golang flag包的具體使用

    本文主要介紹了Golang flag包的具體使用,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2023-02-02
  • Go語(yǔ)言空白表示符_的實(shí)例用法

    Go語(yǔ)言空白表示符_的實(shí)例用法

    在本篇內(nèi)容里小編給大家整理的是一篇關(guān)于Go語(yǔ)言空白表示符_的實(shí)例用法,有興趣的朋友們可以學(xué)習(xí)參考下。
    2021-07-07

最新評(píng)論