gf框架之gdb - 優雅強大的數據庫ORM

文章來源:http://gf.johng.cn/494380

gf框架的數據庫ORM操作由gdb包提供支持,gdb包經過非常精心優雅的設計,提供了非常強大的配置管理、方法操作、鏈式操作、事務操作等功能。gdb包具體API說明文檔詳見:godoc 。本章節對gdb包的使用進行基本的介紹,包括:gdb包基本功能介紹,配置管理功能說明,常見用法及常用操作示例。

使用方式:

import "gitee.com/johng/gf/g/database/gdb"

數據庫配置

gdb數據結構:

type List        []Map                  // 數據記錄列表 
type Map         map[string]interface{} // 數據記錄
type Config      map[string]ConfigGroup // 數據庫配置對象
type ConfigGroup []ConfigNode           // 數據庫分組配置
// 數據庫配置項(一個分組配置對應多個配置項)
type ConfigNode  struct {
    Host     string // 地址
    Port     string // 端口
    User     string // 賬號
    Pass     string // 密碼
    Name     string // 數據庫名稱
    Type     string // 數據庫類型:mysql, sqlite, mssql, pgsql, oracle(目前僅支持mysql,pgsql)
    Role     string // (可選,默認爲master)數據庫的角色,用於主從操作分離,至少需要有一個master,參數值:master, slave
    Charset  string // (可選,默認爲 utf-8)編碼,默認爲 utf-8
    Priority int    // (可選)用於負載均衡的權重計算,當集羣中只有一個節點時,權重沒有任何意義
    Linkinfo string // (可選)自定義鏈接信息,當該字段被設置值時,以上鍊接字段(Host,Port,User,Pass,Name)將失效(該字段是一個擴展功能,參考sql.Open參數)
}

其中,Map和List用於數據表記錄操作,分別對應一條數據表記錄和數據表記錄列表;Config、ConfigGroup及ConfigNode用於數據庫配置管理,ConfigNode用於存儲一個數據庫節點信息,ConfigGroup用於管理多個數據庫節點組成的配置分組(一般一個分組對應一個業務數據庫集羣),Config用於管理多個ConfigGroup配置分組。

gdb主要特點:

  1. 支持多節點數據庫集羣管理,採用單例模式管理數據庫實例化對象;
  2. 支持對數據庫集羣分組管理,按照分組名稱獲取實例化的數據庫操作對象;
  3. 支持多種關係型數據庫管理,可通過ConfigNode.Type屬性進行配置(目前僅支持mysql和pgsql數據庫);
  4. 支持Master-Slave讀寫分離,可通過ConfigNode.Role屬性進行配置;
  5. 支持客戶端的負載均衡管理,可通過ConfigNode.Priority屬性進行配置,值越大,優先級越高;

特別說明,gdb的配置管理最大的特點是,(同一進程中)所有的數據庫集羣信息都使用同一個配置管理模塊進行統一維護,不同業務的數據庫集羣配置使用不同的分組名稱進行配置和獲取。

配置方法

數據庫配置管理方法列表:

// 添加一個數據庫節點到指定的分組中
func AddConfigNode(group string, node ConfigNode)
// 添加一個配置分組到數據庫配置管理中(同名覆蓋)
func AddConfigGroup(group string, nodes ConfigGroup)

// 添加一個數據庫節點到默認的分組中(默認爲default,可修改)
func AddDefaultConfigNode(node ConfigNode)
// 添加一個配置分組到數據庫配置管理中(默認分組爲default,可修改)
func AddDefaultConfigGroup(nodes ConfigGroup)

// 設置數據庫配置爲定義的配置信息
func SetConfig(c Config)
// 設置默認的分組名稱
func SetDefaultGroup(groupName string)

默認分組表示,如果獲取數據庫對象時不指定配置分組名稱,那麼gdb默認讀取的配置分組。例如:gdb.Instance()可獲取一個默認分組的數據庫單例對象。

簡單的做法,我們可以通過gdb包的SetConfig配置管理方法進行自定義的數據庫全局配置,例如:

gdb.SetConfig(gdb.Config {
    "default" : gdb.ConfigGroup {
        gdb.ConfigNode {
            Host     : "127.0.0.1",
            Port     : "3306",
            User     : "root",
            Pass     : "123456",
            Name     : "test",
            Type     : "mysql",
            Role     : "master",
            Priority : 100,
        },
        gdb.ConfigNode {
            Host     : "127.0.0.2",
            Port     : "3306",
            User     : "root",
            Pass     : "123456",
            Name     : "test",
            Type     : "mysql",
            Role     : "master",
            Priority : 100,
        },
    },
})

配置文件

當然,gdb支持配置文件進行配置,這樣也便於項目的配置管理,具體請參見【ORM高級用法】章節。

數據庫操作

gdb數據庫操作的方法比較多,具體詳見godoc,以下僅對一些常用的方法進行介紹。

方法操作

// SQL操作方法,返回原生的標準庫sql對象
Query(query string, args ...interface{}) (*sql.Rows, error)
Exec(query string, args ...interface{}) (sql.Result, error)
Prepare(query string) (*sql.Stmt, error)

// 數據表記錄查詢:
// 查詢單條記錄、查詢多條記錄、查詢單個字段值(鏈式操作同理)
GetAll(query string, args ...interface{}) (List, error)
GetOne(query string, args ...interface{}) (Map, error)
GetValue(query string, args ...interface{}) (interface{}, error)

// 開啓事務操作
Begin() (*Tx, error)

// 數據單條操作
Insert(table string, data Map) (sql.Result, error)
Replace(table string, data Map) (sql.Result, error)
Save(table string, data Map) (sql.Result, error)

// 數據批量操作
BatchInsert(table string, list List, batch int) (sql.Result, error)
BatchReplace(table string, list List, batch int) (sql.Result, error)
BatchSave(table string, list List, batch int) (sql.Result, error)

// 數據修改/刪除
Update(table string, data interface{}, condition interface{}, args ...interface{}) (sql.Result, error)
Delete(table string, condition interface{}, args ...interface{}) (sql.Result, error)

// 創建鏈式操作對象(Table爲From的別名)
Table(tables string) (*DbOp)
From(tables string) (*DbOp)
    
// 關閉數據庫
Close() error

需要說明一下Insert/Replace/Save三者的區別(BatchInsert/BatchReplace/BatchSave同理):

  1. Insert:使用insert into語句進行數據庫寫入,如果寫入的數據中存在Primary Key或者Unique Key的情況,返回失敗,否則寫入一條新數據;
  2. Replace:使用replace into語句進行數據庫寫入,如果寫入的數據中存在Primary Key或者Unique Key的情況,刪除原有記錄,按照給定數據新寫入一條新記錄,否則寫入一條新數據;
  3. Save:使用insert into語句進行數據庫寫入,如果寫入的數據中存在Primary Key或者Unique Key的情況,更新原有數據,否則寫入一條新數據;

鏈式操作

gdb提供簡便靈活的鏈式操作接口,通過數據庫對象的db.Table/db.From方法或者事務對象的tx.Table/tx.From方法基於指定的數據表返回一個鏈式操作對象DbOp,該對象可以執行以下方法(具體方法說明請參考API文檔)。

func LeftJoin(joinTable string, on string) (*DbOp)
func RightJoin(joinTable string, on string) (*DbOp)
func InnerJoin(joinTable string, on string) (*DbOp)

func Fields(fields string) (*DbOp)
func Limit(start int, limit int) (*DbOp)
func Data(data interface{}) (*DbOp)
func Batch(batch int) *DbOp

func Where(where string, args...interface{}) (*DbOp)
func GroupBy(groupby string) (*DbOp)
func OrderBy(orderby string) (*DbOp)

func Insert() (sql.Result, error)
func Replace() (sql.Result, error)
func Save() (sql.Result, error)
func Update() (sql.Result, error)
func Delete() (sql.Result, error)


func Select() (List, error)
func All() (List, error)
func One() (Map, error)
func Value() (interface{}, error)

數據庫示例

https://gitee.com/johng/gf/bl...

方法操作

  1. 獲取ORM單例對象

    // 獲取默認配置的數據庫對象(配置名稱爲"default")
    db, err := gdb.Instance()
    // 獲取配置分組名稱爲"user-center"的數據庫對象
    db, err := gdb.Instance("user-center")
  2. 數據寫入

    r, err := db.Insert("user", gdb.Map {
        "name": "john",
    })
  3. 數據查詢(列表)

    list, err := db.GetAll("select * from user limit 2")
  4. 數據查詢(單條)

    one, err := db.GetOne("select * from user limit 2")
    // 或者
    one, err := db.GetOne("select * from user where uid=1000")
  5. 數據保存

    r, err := db.Save("user", gdb.Map {
        "uid"  :  1,
        "name" : "john",
    })
  6. 批量操作

    // BatchInsert/BatchReplace/BatchSave 同理
    _, err := db.BatchInsert("user", gdb.List {
        {"name": "john_1"},
        {"name": "john_2"},
        {"name": "john_3"},
        {"name": "john_4"},
    }, 10)
  7. 數據更新/刪除

    // db.Update/db.Delete 同理
    r, err := db.Update("user", gdb.Map {"name": "john"}, "uid=?", 10000)
    r, err := db.Update("user", "name='john'", "uid=10000")
    r, err := db.Update("user", "name=?", "uid=?", "john", 10000)

    注意,參數域支持並建議使用預處理模式進行輸入,避免SQL注入風險。

鏈式操作

  1. 鏈式查詢

    // 查詢多條記錄並使用Limit分頁
    r, err := db.Table("user u").LeftJoin("user_detail ud", "u.uid=ud.uid").Fields("u.*, ud.site").Where("u.uid > ?", 1).Limit(0, 10).Select()
    // 查詢符合條件的單條記錄(第一條)
    r, err := db.Table("user u").LeftJoin("user_detail ud", "u.uid=ud.uid").Fields("u.*,ud.site").Where("u.uid=?", 1).One()
    // 查詢字段值
    r, err := db.Table("user u").LeftJoin("user_detail ud", "u.uid=ud.uid").Fields("ud.site").Where("u.uid=?", 1).Value()
    // 分組及排序
    r, err := db.Table("user u").LeftJoin("user_detail ud", "u.uid=ud.uid").Fields("u.*,ud.city").GroupBy("city").OrderBy("register_time asc").Select()
  2. 鏈式更新/刪除

    // 更新
    r, err := db.Table("user").Data(gdb.Map{"name" : "john2"}).Where("name=?", "john").Update()
    r, err := db.Table("user").Data("name='john3'").Where("name=?", "john2").Update()
    // 刪除
    r, err := db.Table("user").Where("uid=?", 10).Delete()
  3. 鏈式寫入/保存

    r, err := db.Table("user").Data(gdb.Map{"name": "john"}).Insert()
    r, err := db.Table("user").Data(gdb.Map{"uid": 10000, "name": "john"}).Replace()
    r, err := db.Table("user").Data(gdb.Map{"uid": 10001, "name": "john"}).Save()
  4. 鏈式批量寫入

    r, err := db.Table("user").Data(gdb.List{
        {"name": "john_1"},
        {"name": "john_2"},
        {"name": "john_3"},
        {"name": "john_4"},
    }).Insert()

    可以指定批量操作中分批寫入數據庫的每批次寫入條數數量:

    r, err := db.Table("user").Data(gdb.List{
        {"name": "john_1"},
        {"name": "john_2"},
        {"name": "john_3"},
        {"name": "john_4"},
    }).Batch(2).Insert()
  5. 鏈式批量保存

    r, err := db.Table("user").Data(gdb.List{
        {"uid":10000, "name": "john_1"},
        {"uid":10001, "name": "john_2"},
        {"uid":10002, "name": "john_3"},
        {"uid":10003, "name": "john_4"},
    }).Save()

事務操作

開啓事務操作可以通過執行db.Begin方法,該方法返回事務的操作對象,類型爲*gdb.Tx,通過該對象執行後續的數據庫操作,並可通過tx.Commit提交修改,或者通過tx.Rollback回滾修改。

  1. 開啓事務操作

    if tx, err := db.Begin(); err == nil {
        fmt.Println("開啓事務操作")
    }

    事務操作對象可以執行所有db對象的方法,具體請參考API文檔

  2. 事務回滾操作

    if tx, err := db.Begin(); err == nil {
        r, err := tx.Save("user", gdb.Map{
            "uid"  :  1,
            "name" : "john",
        })
        tx.Rollback()
        fmt.Println(r, err)
    }
  3. 事務提交操作

    if tx, err := db.Begin(); err == nil {
        r, err := tx.Save("user", gdb.Map{
            "uid"  :  1,
            "name" : "john",
        })
        tx.Commit()
        fmt.Println(r, err)
    }
  4. 事務鏈式操作
    事務操作對象仍然可以通過tx.Table或者tx.From方法返回一個鏈式操作的對象,該對象與db.Table或者db.From方法返回值相同,只不過數據庫操作在事務上執行,可提交或回滾。

    if tx, err := db.Begin(); err == nil {
        r, err := tx.Table("user").Data(gdb.Map{"uid":1, "name": "john_1"}).Save()
        tx.Commit()
        fmt.Println(r, err)
    }

    其他鏈式操作請參考上述鏈式操作章節。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章