gf框架之gdb - 优雅强大的数据库ORM

KOJ 2019-06-26

文章来源: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)
    }

    其他链式操作请参考上述链式操作章节。

相关推荐