Golang使用sqlx操作sqlite3數(shù)據(jù)庫的完整指南
問題提出
幾年前,筆者寫了個golang工程,是和數(shù)據(jù)庫打交道的,當(dāng)時曾經(jīng)調(diào)查過xorm,但并不合適,于是使用較原始的方式實現(xiàn)功能,后來一直沿用。原始方式有一個好處,可以自由組合sql,達(dá)到不同的查詢目的。
最近又要和數(shù)據(jù)庫打交道,考慮到工程實用性,經(jīng)研究,決定使用sqlx將常見功能做成內(nèi)部接口形式。
實踐
設(shè)計說明
本文使用前面文章生成的結(jié)構(gòu)體作為演示數(shù)據(jù)表字段。為簡單起見,id不設(shè)置自增。另外,使用反射手段進(jìn)行建表、寫表、查表等操作。
結(jié)構(gòu)體定義:
type ResultData_t struct {
Id string `json:"id" db:"id"`
Num int `json:"num" db:"num"`
Etype int `json:"etype" db:"etype"`
Timestamp string `json:"timestamp" db:"timestamp"`
MCode string `json:"mCode" db:"mCode"`
MName string `json:"mName" db:"mName"`
Age string `json:"age" db:"age"`
Addr string `json:"addr" db:"addr"`
Mac string `json:"mac" db:"mac"`
Remark string `json:"remark" db:"remark"`
}
變量定義:
var gTableName = "result_data" var dbstr = "file:./database/result_data.db3?_journal_mode=WAL&_busy_timeout=5000&_foreign_keys=on"
引入第三方庫
庫如下:
"github.com/jmoiron/sqlx" _ "modernc.org/sqlite"
要說明的是,本文采用"modernc.org/sqlite",這個庫實現(xiàn)了sqlite3,但不使用cgo,因此十分合適跨平臺,注意,該庫使用的數(shù)據(jù)庫驅(qū)動名為sqlite,而不是sqlite3。
筆者近年使用go作為主力語言,一個工程,如果不涉及底層庫,都會優(yōu)先考慮跨平臺性。比如,同一套代碼,在windows開發(fā)調(diào)試,再交叉編譯成linux或arm版本,基本不用開虛擬機(jī)了。
初始化數(shù)據(jù)庫
代碼如下,代碼會自動創(chuàng)建database/result_data.db3文件,默認(rèn)表為result_data。
type ResultWriter struct {
db *sqlx.DB
}
func NewDB3Writer() *ResultWriter {
return &ResultWriter{}
}
func getDB3File(dbname string) string {
if !strings.HasPrefix(dbname, "file:") {
return ""
}
filePart := dbname[5:] // 移除 "file:"
parts := strings.Split(filePart, "?")
if len(parts) == 0 {
return ""
}
return com.GetFileDir(parts[0])
}
func (w *ResultWriter) CreateResultWriter(dbname string) (err error) {
// SQLite 連接字符串(包含并發(fā)優(yōu)化參數(shù))
// "file:./database/result_data.db3?_journal_mode=WAL&_busy_timeout=5000&_foreign_keys=on"
// 確保目錄存在
filePath := getDB3File(dbname)
com.MkDir(filePath)
dsn := dbname
w.db, err = sqlx.Connect("sqlite", dsn)
if err != nil {
return err
}
w.db.SetMaxOpenConns(1) // SQLite 推薦單個連接
w.db.SetMaxIdleConns(1)
w.db.SetConnMaxLifetime(time.Hour)
err = w.initTables()
if err != nil {
klog.Println("創(chuàng)建內(nèi)部數(shù)據(jù)庫失敗: ", err.Error())
}
return nil
}
func (w *ResultWriter) initTables() error {
// w.initTableSimple()
// 使用反射創(chuàng)建表
err := dbutil.CreateTableFromStruct(w.db, gTableName, ResultData_t{}, "id")
if err != nil {
return fmt.Errorf("創(chuàng)建 result_data 表失敗: %v", err)
}
return nil
}
建表
在初始化的同時創(chuàng)建了數(shù)據(jù)表。可以調(diào)用initTableSimple,也可以調(diào)用CreateTableFromStruct。其中initTableSimple為直接指定字段創(chuàng)建表,實現(xiàn)函數(shù)如下:
// 直接指定字段創(chuàng)建表
func (w *ResultWriter) initTableSimple() error {
createTableSQL := `
CREATE TABLE IF NOT EXISTS result_data (
id TEXT PRIMARY KEY,
num INTEGER NOT NULL,
etype INTEGER NOT NULL,
timestamp TEXT NOT NULL,
mCode TEXT NOT NULL,
mName TEXT NOT NULL,
age TEXT NOT NULL,
addr TEXT NOT NULL,
mac TEXT NOT NULL,
remark TEXT
)
`
_, err := w.db.Exec(createTableSQL)
if err != nil {
return err
}
// 創(chuàng)建常用索引
indexes := []string{
"CREATE INDEX IF NOT EXISTS idx_result_data_timestamp ON result_data(timestamp)",
}
for _, indexSQL := range indexes {
_, err = w.db.Exec(indexSQL)
if err != nil {
return fmt.Errorf("創(chuàng)建索引失敗: %v", err)
}
}
return nil
}
CreateTableFromStruct則使用結(jié)構(gòu)體建表,通用性強(qiáng)一些,實現(xiàn)如下:
func CreateTableFromStruct(db *sqlx.DB, tableName string, model interface{}, primaryKey string, indices ...string) error {
if db == nil {
return fmt.Errorf("數(shù)據(jù)庫連接為空")
}
var t reflect.Type
if model == nil {
return fmt.Errorf("model 參數(shù)不能為 nil")
}
v := reflect.ValueOf(model)
if v.Kind() == reflect.Ptr {
v = v.Elem()
t = v.Type()
} else {
t = reflect.TypeOf(model)
}
if t.Kind() != reflect.Struct {
return fmt.Errorf("model 必須是結(jié)構(gòu)體")
}
var columns []string
var hasPrimaryKey bool
// 遍歷結(jié)構(gòu)體字段
for i := 0; i < t.NumField(); i++ {
field := t.Field(i)
// 獲取字段名(優(yōu)先使用 db tag,其次使用字段名)
columnName := field.Tag.Get("db")
if columnName == "" || columnName == "-" {
// 如果 db tag 為空或為 "-",跳過該字段
continue
}
// 獲取字段類型
fieldType := field.Type
// 處理指針類型
if fieldType.Kind() == reflect.Ptr {
fieldType = fieldType.Elem()
}
// 根據(jù)字段類型確定 SQL 類型
var sqlType string
switch fieldType.Kind() {
case reflect.String:
sqlType = "TEXT"
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
sqlType = "INTEGER"
case reflect.Float32, reflect.Float64:
sqlType = "REAL"
case reflect.Bool:
sqlType = "INTEGER" // SQLite 用 0/1 表示布爾值
case reflect.Struct:
// 處理 time.Time 類型
if fieldType.String() == "time.Time" {
sqlType = "TEXT"
} else {
sqlType = "TEXT" // 其他結(jié)構(gòu)體序列化為 JSON 存儲
}
case reflect.Slice, reflect.Array, reflect.Map:
// 數(shù)組/切片/映射序列化為 JSON 存儲
sqlType = "TEXT"
default:
// 默認(rèn)使用 TEXT 類型
sqlType = "TEXT"
}
// 構(gòu)建列定義
columnDef := fmt.Sprintf("%s %s", columnName, sqlType)
// 檢查是否是主鍵
// isPrimaryKey := false
if primaryKey != "" {
// 使用指定的主鍵
if columnName == primaryKey {
columnDef += " PRIMARY KEY"
// isPrimaryKey = true
hasPrimaryKey = true
}
} else {
// 自動判斷:如果字段名為 "id" 且沒有顯式指定主鍵
if columnName == "id" && !hasPrimaryKey {
columnDef += " PRIMARY KEY AUTOINCREMENT"
// isPrimaryKey = true
hasPrimaryKey = true
}
}
// 檢查 NOT NULL 約束
notNullTag := field.Tag.Get("dbNotNull")
if notNullTag == "true" || notNullTag == "1" {
columnDef += " NOT NULL"
}
// 檢查 UNIQUE 約束
uniqueTag := field.Tag.Get("dbUnique")
if uniqueTag == "true" || uniqueTag == "1" {
columnDef += " UNIQUE"
}
// 添加 DEFAULT 值
defaultTag := field.Tag.Get("dbDefault")
if defaultTag != "" {
columnDef += " DEFAULT " + defaultTag
}
columns = append(columns, columnDef)
}
if len(columns) == 0 {
return fmt.Errorf("沒有找到有效的字段")
}
// 建表語句
createTableSQL := fmt.Sprintf(`
CREATE TABLE IF NOT EXISTS %s (
%s
)
`, tableName, strings.Join(columns, ",\n\t\t"))
// 執(zhí)行
_, err := db.Exec(createTableSQL)
if err != nil {
return fmt.Errorf("創(chuàng)建表 %s 失敗: %v", tableName, err)
}
// fmt.Printf("debug 創(chuàng)建表語句:[%v]\n", createTableSQL)
// 創(chuàng)建索引
for _, field := range indices {
if field != "" {
indexSQL := fmt.Sprintf("CREATE INDEX IF NOT EXISTS idx_%s_%s ON %s(%s)",
tableName, field, tableName, field)
_, err = db.Exec(indexSQL)
if err != nil {
return fmt.Errorf("創(chuàng)建索引 %s 失敗: %v", field, err)
}
}
}
return nil
}
寫表
func InsertTable[T any](db *sqlx.DB, tableName string, model []T, skipid ...string) (int64, string, error) {
if len(model) == 0 {
return 0, "", nil
}
// 獲取第一個記錄的結(jié)構(gòu)信息
t := reflect.TypeOf(model[0])
if t.Kind() == reflect.Ptr {
t = t.Elem()
}
// 可指定過濾的字段名,有的庫可能會使用自增的id,因此要過濾
getskip := func(id string) bool {
for i := range skipid {
if skipid[i] == id {
return true
}
}
return false
}
// 收集所有 db tag(排除 id 字段)
var fieldNames []string
var dbTags []string
for i := 0; i < t.NumField(); i++ {
field := t.Field(i)
dbTag := field.Tag.Get("db")
if dbTag != "" && dbTag != "-" && !getskip(dbTag) {
fieldNames = append(fieldNames, field.Name)
dbTags = append(dbTags, dbTag)
}
}
// 構(gòu)建帶命名參數(shù)的 SQL
var namedParams []string
for _, tag := range dbTags {
namedParams = append(namedParams, ":"+tag)
}
query := fmt.Sprintf(`
INSERT INTO %s (%s)
VALUES (%s)
`, tableName,
strings.Join(dbTags, ", "),
strings.Join(namedParams, ", "))
// 執(zhí)行批量插入
result, err := db.NamedExec(query, model)
if err != nil {
return 0, query, fmt.Errorf("表 %s: %v", tableName, err)
}
// fmt.Printf("debug insert: [%v]\n", query)
rows, err := result.RowsAffected()
if err != nil {
return int64(len(model)), query, nil // 有些數(shù)據(jù)庫可能不支持
}
return rows, query, nil
}
查表
查表同樣使用了反射的技術(shù),主要調(diào)用genSelectItem將結(jié)構(gòu)體各字段組成select語句。
比如:
SELECT id, num, etype, timestamp, mCode, mName, age, addr, mac, remark FROM result_data WHERE id = 'id_8' SELECT id, num, etype, timestamp, mCode, mName, age, addr, mac, remark FROM result_data
事實上,在結(jié)構(gòu)體ResultData_t定義時,就使用了db指定了各字段的名稱,如下:
Id string `json:"id" db:"id"` Num int `json:"num" db:"num"`
這也是genSelectItem可處理任意結(jié)構(gòu)體的原因,該函數(shù)見下文的實現(xiàn)。
查表分2類,一是單個查詢QueryOne,調(diào)用者知道只有一個結(jié)果時,可使用該函數(shù)。如果查詢多個,則使用QueryTable函數(shù)。
這2個函數(shù)實現(xiàn)如下:
func QueryOne[T any](db *sqlx.DB, tableName string, model T, where string, args ...interface{}) (*T, error) {
sqlstr := genSelectItem(model, tableName, where)
var result T
err := db.Get(&result, sqlstr, args...)
if err != nil {
return nil, fmt.Errorf("表 %s: %v", tableName, err)
}
return &result, nil
}
func QueryTable[T any](db *sqlx.DB, tableName string, model T, where string) ([]T, string, error) {
mysqlstr := fmt.Sprintf("SELECT * FROM %v %v", tableName, where)
sqlstr := genSelectItem(model, tableName, where)
var results []T
err := db.Select(&results, sqlstr)
if err != nil {
return nil, mysqlstr, fmt.Errorf("表 %s: %v", tableName, err)
}
return results, mysqlstr, nil
}
接口工具函數(shù)集
本文所用的工具函數(shù)集如下:
package db
import (
"fmt"
"reflect"
"strings"
"github.com/jmoiron/sqlx"
)
func CreateTableFromStruct(db *sqlx.DB, tableName string, model interface{}, primaryKey string, indices ...string) error {
if db == nil {
return fmt.Errorf("數(shù)據(jù)庫連接為空")
}
var t reflect.Type
if model == nil {
return fmt.Errorf("model 參數(shù)不能為 nil")
}
v := reflect.ValueOf(model)
if v.Kind() == reflect.Ptr {
v = v.Elem()
t = v.Type()
} else {
t = reflect.TypeOf(model)
}
if t.Kind() != reflect.Struct {
return fmt.Errorf("model 必須是結(jié)構(gòu)體")
}
var columns []string
var hasPrimaryKey bool
// 遍歷結(jié)構(gòu)體字段
for i := 0; i < t.NumField(); i++ {
field := t.Field(i)
// 獲取字段名(優(yōu)先使用 db tag,其次使用字段名)
columnName := field.Tag.Get("db")
if columnName == "" || columnName == "-" {
// 如果 db tag 為空或為 "-",跳過該字段
continue
}
// 獲取字段類型
fieldType := field.Type
// 處理指針類型
if fieldType.Kind() == reflect.Ptr {
fieldType = fieldType.Elem()
}
// 根據(jù)字段類型確定 SQL 類型
var sqlType string
switch fieldType.Kind() {
case reflect.String:
sqlType = "TEXT"
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
sqlType = "INTEGER"
case reflect.Float32, reflect.Float64:
sqlType = "REAL"
case reflect.Bool:
sqlType = "INTEGER" // SQLite 用 0/1 表示布爾值
case reflect.Struct:
// 處理 time.Time 類型
if fieldType.String() == "time.Time" {
sqlType = "TEXT"
} else {
sqlType = "TEXT" // 其他結(jié)構(gòu)體序列化為 JSON 存儲
}
case reflect.Slice, reflect.Array, reflect.Map:
// 數(shù)組/切片/映射序列化為 JSON 存儲
sqlType = "TEXT"
default:
// 默認(rèn)使用 TEXT 類型
sqlType = "TEXT"
}
// 構(gòu)建列定義
columnDef := fmt.Sprintf("%s %s", columnName, sqlType)
// 檢查是否是主鍵
// isPrimaryKey := false
if primaryKey != "" {
// 使用指定的主鍵
if columnName == primaryKey {
columnDef += " PRIMARY KEY"
// isPrimaryKey = true
hasPrimaryKey = true
}
} else {
// 自動判斷:如果字段名為 "id" 且沒有顯式指定主鍵
if columnName == "id" && !hasPrimaryKey {
columnDef += " PRIMARY KEY AUTOINCREMENT"
// isPrimaryKey = true
hasPrimaryKey = true
}
}
// 檢查 NOT NULL 約束
notNullTag := field.Tag.Get("dbNotNull")
if notNullTag == "true" || notNullTag == "1" {
columnDef += " NOT NULL"
}
// 檢查 UNIQUE 約束
uniqueTag := field.Tag.Get("dbUnique")
if uniqueTag == "true" || uniqueTag == "1" {
columnDef += " UNIQUE"
}
// 添加 DEFAULT 值
defaultTag := field.Tag.Get("dbDefault")
if defaultTag != "" {
columnDef += " DEFAULT " + defaultTag
}
columns = append(columns, columnDef)
}
if len(columns) == 0 {
return fmt.Errorf("沒有找到有效的字段")
}
// 建表語句
createTableSQL := fmt.Sprintf(`
CREATE TABLE IF NOT EXISTS %s (
%s
)
`, tableName, strings.Join(columns, ",\n\t\t"))
// 執(zhí)行
_, err := db.Exec(createTableSQL)
if err != nil {
return fmt.Errorf("創(chuàng)建表 %s 失敗: %v", tableName, err)
}
// fmt.Printf("debug 創(chuàng)建表語句:[%v]\n", createTableSQL)
// 創(chuàng)建索引
for _, field := range indices {
if field != "" {
indexSQL := fmt.Sprintf("CREATE INDEX IF NOT EXISTS idx_%s_%s ON %s(%s)",
tableName, field, tableName, field)
_, err = db.Exec(indexSQL)
if err != nil {
return fmt.Errorf("創(chuàng)建索引 %s 失敗: %v", field, err)
}
}
}
return nil
}
// 自動從結(jié)構(gòu)體生成SELECT字段列表
func genSelectItem(model interface{}, tableName string, where1 ...string) string {
t := reflect.TypeOf(model)
if t.Kind() == reflect.Ptr {
t = t.Elem()
}
var fields []string
for i := 0; i < t.NumField(); i++ {
field := t.Field(i)
dbTag := field.Tag.Get("db")
if dbTag != "" && dbTag != "-" {
fields = append(fields, dbTag)
}
}
where := ""
if len(where1) > 0 {
where = where1[0]
}
return fmt.Sprintf("SELECT %s FROM %s %s", strings.Join(fields, ", "), tableName, where)
}
///////////////////////////
func QueryOne[T any](db *sqlx.DB, tableName string, model T, where string, args ...interface{}) (*T, error) {
sqlstr := genSelectItem(model, tableName, where)
var result T
err := db.Get(&result, sqlstr, args...)
if err != nil {
// if err == sql.ErrNoRows {
// return nil, err // 沒有找到記錄,返回 nil
// }
return nil, fmt.Errorf("表 %s: %v", tableName, err)
}
return &result, nil
}
func QueryTable[T any](db *sqlx.DB, tableName string, model T, where string) ([]T, string, error) {
// start := time.Now()
// defer func() {
// stats := db.Stats()
// fmt.Printf("查詢耗時: %v, 等待連接數(shù)量: %d\n",
// time.Since(start), stats.WaitCount)
// }()
mysqlstr := fmt.Sprintf("SELECT * FROM %v %v", tableName, where)
sqlstr := genSelectItem(model, tableName, where)
var results []T
err := db.Select(&results, sqlstr)
if err != nil {
return nil, mysqlstr, fmt.Errorf("表 %s: %v", tableName, err)
}
return results, mysqlstr, nil
}
///////////////////
// BatchInsert 通用批量插入函數(shù)
func InsertTable[T any](db *sqlx.DB, tableName string, model []T, skipid ...string) (int64, string, error) {
if len(model) == 0 {
return 0, "", nil
}
// 獲取第一個記錄的結(jié)構(gòu)信息
t := reflect.TypeOf(model[0])
if t.Kind() == reflect.Ptr {
t = t.Elem()
}
// 可指定過濾的字段名,有的庫可能會使用自增的id,因此要過濾
getskip := func(id string) bool {
for i := range skipid {
if skipid[i] == id {
return true
}
}
return false
}
// 收集所有 db tag(排除 id 字段)
var fieldNames []string
var dbTags []string
for i := 0; i < t.NumField(); i++ {
field := t.Field(i)
dbTag := field.Tag.Get("db")
if dbTag != "" && dbTag != "-" && !getskip(dbTag) {
fieldNames = append(fieldNames, field.Name)
dbTags = append(dbTags, dbTag)
}
}
// 構(gòu)建帶命名參數(shù)的 SQL
var namedParams []string
for _, tag := range dbTags {
namedParams = append(namedParams, ":"+tag)
}
query := fmt.Sprintf(`
INSERT INTO %s (%s)
VALUES (%s)
`, tableName,
strings.Join(dbTags, ", "),
strings.Join(namedParams, ", "))
// 執(zhí)行批量插入
result, err := db.NamedExec(query, model)
if err != nil {
return 0, query, fmt.Errorf("表 %s: %v", tableName, err)
}
// fmt.Printf("debug insert: [%v]\n", query)
rows, err := result.RowsAffected()
if err != nil {
return int64(len(model)), query, nil // 有些數(shù)據(jù)庫可能不支持
}
return rows, query, nil
}
// ////////////////
// QueryCount 查詢記錄數(shù)量
func QueryCount(db *sqlx.DB, tableName, where string, args ...interface{}) (int64, error) {
sqlstr := fmt.Sprintf("SELECT COUNT(*) FROM %s", tableName)
if where != "" {
sqlstr += " WHERE " + where
}
var count int64
err := db.Get(&count, sqlstr, args...)
if err != nil {
return 0, fmt.Errorf("表 %s: %v", tableName, err)
}
return count, nil
}
測試
建表打印信息:
debug 創(chuàng)建表語句:[
CREATE TABLE IF NOT EXISTS result_data (
id TEXT PRIMARY KEY,
num INTEGER,
etype INTEGER,
timestamp TEXT,
mCode TEXT,
mName TEXT,
age TEXT,
addr TEXT,
mac TEXT,
remark TEXT
)
]
插入表打印信息:
debug insert: [
INSERT INTO result_data (id, num, etype, timestamp, mCode, mName, age, addr, mac, remark)
VALUES (:id, :num, :etype, :timestamp, :mCode, :mName, :age, :addr, :mac, :remark)
]
數(shù)據(jù)表內(nèi)容:

查表打印信息:
[2025-12-30 20:29:23 866] [INFO] query result: id_8 2025-12-30T20:29:23
[2025-12-30 20:29:23 867] [INFO] query results:
[2025-12-30 20:29:23 868] [INFO] id_0 2025-12-30T20:29:23
[2025-12-30 20:29:23 868] [INFO] id_1 2025-12-30T20:29:23
[2025-12-30 20:29:23 868] [INFO] id_2 2025-12-30T20:29:23
[2025-12-30 20:29:23 869] [INFO] id_3 2025-12-30T20:29:23
[2025-12-30 20:29:23 869] [INFO] id_4 2025-12-30T20:29:23
[2025-12-30 20:29:23 869] [INFO] id_5 2025-12-30T20:29:23
[2025-12-30 20:29:23 870] [INFO] id_6 2025-12-30T20:29:23
[2025-12-30 20:29:23 871] [INFO] id_7 2025-12-30T20:29:23
[2025-12-30 20:29:23 871] [INFO] id_8 2025-12-30T20:29:23
有插入數(shù)據(jù)的寫表打印信息:
[2025-12-30 20:30:16 233] [INFO] insert sqlite failed: 表 result_data: constraint failed: UNIQUE constraint failed: result_data.id (1555)
[2025-12-30 20:30:16 234] [INFO] query result: id_8 2025-12-30T20:29:23
[2025-12-30 20:30:16 234] [INFO] query results:
[2025-12-30 20:30:16 235] [INFO] id_0 2025-12-30T20:29:23
[2025-12-30 20:30:16 236] [INFO] id_1 2025-12-30T20:29:23
[2025-12-30 20:30:16 236] [INFO] id_2 2025-12-30T20:29:23
[2025-12-30 20:30:16 236] [INFO] id_3 2025-12-30T20:29:23
[2025-12-30 20:30:16 237] [INFO] id_4 2025-12-30T20:29:23
[2025-12-30 20:30:16 237] [INFO] id_5 2025-12-30T20:29:23
[2025-12-30 20:30:16 237] [INFO] id_6 2025-12-30T20:29:23
[2025-12-30 20:30:16 238] [INFO] id_7 2025-12-30T20:29:23
[2025-12-30 20:30:16 238] [INFO] id_8 2025-12-30T20:29:23
小結(jié)
本文通過示例工程展示了sqlx操作sqlite數(shù)據(jù)庫的方法,理論上只要數(shù)據(jù)庫驅(qū)動合適,sqlx可以對任意數(shù)據(jù)庫進(jìn)行操作,在代碼統(tǒng)一了方法,便于維護(hù)。
以上就是Golang使用sqlx操作sqlite3數(shù)據(jù)庫的完整指南的詳細(xì)內(nèi)容,更多關(guān)于Golang操作sqlite3的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
實現(xiàn)像php一樣方便的go ORM數(shù)據(jù)庫操作示例詳解
這篇文章主要為大家介紹了實現(xiàn)像php一樣方便的go ORM數(shù)據(jù)庫操作示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-12-12
Go語言設(shè)計模式之實現(xiàn)觀察者模式解決代碼臃腫
今天學(xué)習(xí)一下用?Go?實現(xiàn)觀察者模式,觀察者模式主要是用來實現(xiàn)事件驅(qū)動編程。事件驅(qū)動編程的應(yīng)用還是挺廣的,除了我們都知道的能夠用來解耦:用戶修改密碼后,給用戶發(fā)短信進(jìn)行風(fēng)險提示之類的典型場景,在微服務(wù)架構(gòu)實現(xiàn)最終一致性、實現(xiàn)事件源A?+?ES2022-08-08
Go語言strconv包實現(xiàn)字符串和數(shù)值類型的相互轉(zhuǎn)換
這篇文章主要介紹了Go語言strconv包實現(xiàn)字符串和數(shù)值類型的相互轉(zhuǎn)換,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-03-03
Golang連接并操作PostgreSQL數(shù)據(jù)庫基本操作
PostgreSQL是常見的免費的大型關(guān)系型數(shù)據(jù)庫,具有豐富的數(shù)據(jù)類型,也是軟件項目常用的數(shù)據(jù)庫之一,下面這篇文章主要給大家介紹了關(guān)于Golang連接并操作PostgreSQL數(shù)據(jù)庫基本操作的相關(guān)資料,需要的朋友可以參考下2022-09-09

