• 链式操作
    • Insert/Replace/Save
    • Data方法
    • Where方法
  • 链式安全
    • 默认情况
    • Safe方法
    • Clone方法
  • Struct参数传递
  • Struct结果输出

    gdb链式操作使用方式简单灵活,是官方推荐的数据库操作方式。

    链式操作

    链式操作可以通过数据库对象的db.Table/db.From方法或者事务对象的tx.Table/tx.From方法,基于指定的数据表返回一个链式操作对象*Model,该对象可以执行以下方法。

    接口文档:https://godoc.org/github.com/gogf/gf/g/database/gdb#Model

    1. func (md *Model) LeftJoin(joinTable string, on string) *Model
    2. func (md *Model) RightJoin(joinTable string, on string) *Model
    3. func (md *Model) InnerJoin(joinTable string, on string) *Model
    4. func (md *Model) Fields(fields string) *Model
    5. func (md *Model) Limit(start int, limit int) *Model
    6. func (md *Model) Data(data...interface{}) *Model
    7. func (md *Model) Batch(batch int) *Model
    8. func (md *Model) Filter() *Model
    9. func (md *Model) Safe(safe...bool) *Model
    10. func (md *Model) Where(where interface{}, args...interface{}) *Model
    11. func (md *Model) And(where interface{}, args ...interface{}) *Model
    12. func (md *Model) Or(where interface{}, args ...interface{}) *Model
    13. func (md *Model) GroupBy(groupby string) *Model
    14. func (md *Model) OrderBy(orderby string) *Model
    15. func (md *Model) Insert() (sql.Result, error)
    16. func (md *Model) Replace() (sql.Result, error)
    17. func (md *Model) Save() (sql.Result, error)
    18. func (md *Model) Update() (sql.Result, error)
    19. func (md *Model) Delete() (sql.Result, error)
    20. func (md *Model) Select() (Result, error)
    21. func (md *Model) All() (Result, error)
    22. func (md *Model) One() (Record, error)
    23. func (md *Model) Value() (Value, error)
    24. func (md *Model) Count() (int, error)
    25. func (md *Model) Struct(objPointer interface{}) error
    26. func (md *Model) Structs(objPointerSlice interface{}) error
    27. func (md *Model) Scan(objPointer interface{}) error
    28. func (md *Model) Chunk(limit int, callback func(result Result, err error) bool)
    29. func (md *Model) ForPage(page, limit int) (*Model)

    Insert/Replace/Save

    1. Insert 使用insert into语句进行数据库写入,如果写入的数据中存在Primary Key或者Unique Key的情况,返回失败,否则写入一条新数据;
    2. Replace 使用replace into语句进行数据库写入,如果写入的数据中存在Primary Key或者Unique Key的情况,会删除原有的记录,必定会写入一条新记录;
    3. Save 使用insert into语句进行数据库写入,如果写入的数据中存在Primary Key或者Unique Key的情况,更新原有数据,否则写入一条新数据;

    Data方法

    Data方法用于传递数据参数,用于数据写入/更新等写操作,支持的参数为string/map/slice/struct/*struct。例如,在进行Insert操作时,开发者可以传递任意的map类型,如: map[string]string/map[string]interface{}/map[interface{}]interface{}等等,也可以传递任意的struct对象或者其指针*struct

    Where方法

    Where(包括And/Or)方法用于传递查询条件参数,支持的参数为任意的string/map/slice/struct/*struct类型。

    链式安全

    默认情况

    在默认情况下,gdb非链式安全的,也就是说链式操作的每一个方法都将对当前操作的Model属性进行修改,因此该Model对象不可以重复使用。例如,当存在多个分开查询的条件时,我们可以这么来使用Model对象:

    1. user := g.DB().Table("user")
    2. user.Where("status IN(?)", g.Slice{1,2,3})
    3. if vip {
    4. // 查询条件自动叠加,修改当前模型对象
    5. user.Where("money>=?", 1000000)
    6. } else {
    7. // 查询条件自动叠加,修改当前模型对象
    8. user.Where("money<?", 1000000)
    9. }
    10. // vip: SELECT * FROM user WHERE status IN(1,2,3) AND money >= 1000000
    11. // !vip: SELECT * FROM user WHERE status IN(1,2,3) AND money < 1000000
    12. r, err := user.Select()

    可以看到,如果是分开执行链式操作,链式的每一个操作都会修改已有的Model对象,查询条件会自动叠加。这种使用方式中,每次我们需要操作user用户表,都得使用g.DB().Table("user")这样的语法创建一个新的用户模型对象,相对来说会比较繁琐。

    默认情况下,基于性能以及GC优化考虑,模型对象为非链式安全,防止产生过多的临时模型对象。

    Safe方法

    当然,我们可以通过Safe方法设置当前模型为链式安全的对象,后续的每一个链式操作都将返回一个新的Model对象,该Model对象可重复使用。

    1. // 定义一个用户模型单例
    2. user := g.DB().Table("user").Safe()
    1. m := m.Where("status IN(?)", g.Slice{1,2,3})
    2. if vip {
    3. // 查询条件通过赋值叠加
    4. m = m.And("money>=?", 1000000)
    5. } else {
    6. // 查询条件通过赋值叠加
    7. m = m.And("money<?", 1000000)
    8. }
    9. r, err := m.Select()

    可以看到,示例中得用户模型单例对象user可以重复使用,而不用担心被“污染”的问题。在这种链式安全的方式下,我们可以创建一个用户单例对象user,并且可以重复使用到后续的各种查询中。存在多个查询条件时,条件的叠加需要通过模型赋值操作(m = m.xxx)来实现。

    使用Safe方法标记之后,每一个链式操作都将会创建一个新的临时模型对象,从而实现链式安全。这种使用方式在模型操作中比较常见。

    Clone方法

    此外,我们也可以使用Clone方法克隆当前模型,创建一个新的模型来实现链式安全,由于是新的模型对象,因此并不担心会修改已有的模型对象的问题。多个查询条件的叠加无需模型赋值。例如:

    1. // 定义一个用户模型单例
    2. user := g.DB().Table("user")
    1. // 克隆一个新的用户模型
    2. m := user.Clone()
    3. m.Where("status IN(?)", g.Slice{1,2,3})
    4. if vip {
    5. m.And("money>=?", 1000000)
    6. } else {
    7. m.And("money<?", 1000000)
    8. }
    9. r, err := m.Select()

    Struct参数传递

    gdb中,Data/Where/And/Or链式方法支持任意的string/map/slice/struct/*struct数据类型参数,该特性为gdb提供了很高的灵活性。当使用struct/*struct对象作为输入参数时,将会被自动解析为map类型,只有struct公开属性能够被转换,并且支持 gconv/json 标签,用于定义转换后的键名,即与表字段的映射关系。

    例如:

    1. type User struct {
    2. Uid int `gconv:"user_id"`
    3. Name string `gconv:"user_name"`
    4. NickName string `gconv:"nick_name"`
    5. }
    6. // 或者
    7. type User struct {
    8. Uid int `json:"user_id"`
    9. Name string `json:"user_name"`
    10. NickName string `json:"nick_name"`
    11. }

    其中,struct的属性应该是公开属性(首字母大写),gconv标签对应的是数据表的字段名称。表字段的对应关系标签既可以使用gconv,也可以使用传统的json标签,但是当两种标签都存在时,gconv标签的优先级更高。为避免将struct对象转换为JSON数据格式返回时与JSON编码标签冲突,推荐使用gconv标签来实现映射关系。具体转换规则请查看【gconv.Map转换】章节。

    Struct结果输出

    链式操作提供了3个方法对查询结果执行struct对象转换/输出。

    1. Struct: 将查询结果转换为一个struct对象,查询结果应当是特定的一条记录,并且pointer参数应当为struct对象的指针地址(*struct或者**struct),使用方式例如:
      1. type User struct {
      2. Id int
      3. Passport string
      4. Password string
      5. NickName string
      6. CreateTime gtime.Time
      7. }
      8. user := new(User)
      9. err := db.Table("user").Where("id", 1).Struct(user)
      或者
      1. user := &User{}
      2. err := db.Table("user").Where("id", 1).Struct(user)
      前两种方式都是预先初始化对象(提前分配内存),推荐的方式:
      1. user := (*User)(nil)
      2. err := db.Table("user").Where("id", 1).Struct(&user)
      这种方式只有在查询到数据的时候才会执行初始化及内存分配。注意在用法上的区别,特别是传递参数类型的差别(前两种方式传递的参数类型是*User,这里传递的参数类型其实是**User)。
    2. Structs: 将多条查询结果集转换为一个[]struct/[]*struct数组,查询结果应当是多条记录组成的结果集,并且pointer应当为数组的指针地址,使用方式例如:
      1. users := ([]User)(nil)
      2. // 或者 var users []User
      3. err := db.Table("user").Structs(&users)
      或者
      1. users := ([]*User)(nil)
      2. // 或者 var user []*User
      3. err := db.Table("user").Structs(&users)
    3. Scan: 该方法会根据输入参数pointer的类型选择调用Struct还是Structs方法。如果结果是特定的一条记录,那么调用Struct方法;如果结果是slice类型则调用Structs方法。