引言
在軟件開發中,數據庫是管理和存儲數據的關鍵組件。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 數據庫文件。以下是一個簡單的示例:
package mainimport ("database/sql""fmt""path/filepath"_ "github.com/mattn/go-sqlite3"
)const defDBPath = "./data"func initContDB() error {dbFile := filepath.Join(defDBPath, "test.db")var err errorgDB, 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 introw.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
表。
增刪改查操作
插入數據
插入數據可以使用Prepare
和Exec
方法。以下是插入的示例代碼:
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
方法用于執行插入操作。
更新數據
更新數據的操作與插入數據類似,同樣使用Prepare
和Exec
方法。以下是更新的示例代碼:
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 := falserow := smt.QueryRow(ct.Name)if row != nil {var name stringerr = 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}}
}
在上述代碼中,首先檢查數據是否存在,如果存在則更新數據,否則插入新數據。
刪除數據
刪除數據可以使用Prepare
和Exec
方法執行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_info
和t_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 ContItemrows.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 數據庫。在實際開發中,根據具體需求選擇合適的操作方法和優化策略,可以提高程序的性能和穩定性。