数据结构

查询结果的数据结构如下:

  1. type Value = *gvar.Var // 返回数据表记录值
  2. type Record map[string]Value // 返回数据表记录键值对
  3. type Result []Record // 返回数据表记录列表
  1. Value/Record/Result用于ORM操作的结果数据类型。
  2. Result表示数据表记录列表,Record表示一条数据表记录,Value表示记录中的一条键值数据。
  3. Value*gvar.Var类型的别名类型,方便于后续的数据类型转换。

Record记录处理

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

  1. // 数据表记录
  2. type Record
  3. func (r Record) GMap() *gmap.StrAnyMap
  4. func (r Record) IsEmpty() bool
  5. func (r Record) Map() Map
  6. func (r Record) Xml(rootTag ...string) string
  7. func (r Record) Json() string
  8. func (r Record) Struct(pointer interface{}) error

gdb为数据表记录操作提供了极高的灵活性和简便性,除了支持以map的形式访问/操作数据表记录以外,也支持将数据表记录转换为struct进行处理。我们以下使用一个简单的示例来演示该特性。

首先,我们的用户表结构是这样的(简单设计的示例表):

  1. CREATE TABLE `user` (
  2. `uid` int(10) unsigned NOT NULL AUTO_INCREMENT,
  3. `name` varchar(30) NOT NULL DEFAULT '' COMMENT '昵称',
  4. `site` varchar(255) NOT NULL DEFAULT '' COMMENT '主页',
  5. PRIMARY KEY (`uid`)
  6. ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

其次,我们的表数据如下:

  1. uid name site
  2. 1 john https://goframe.org

最后,我们的示例程序如下:

  1. package main
  2. import (
  3. "database/sql"
  4. "github.com/gogf/gf/frame/g"
  5. )
  6. type User struct {
  7. Uid int
  8. Name string
  9. }
  10. func main() {
  11. var user *User
  12. err := g.DB().Table("user").Where("uid", 1).Scan(&user)
  13. if err != nil && err != sql.ErrNoRows {
  14. g.Log().Header(false).Fatal(err)
  15. }
  16. if user != nil {
  17. g.Log().Header(false).Println(user)
  18. }
  19. }

执行后,输出结果为:

  1. {"Uid":1,"Name":"john"}

这里,我们自定义了一个struct,里面只包含了UidName属性,可以看到它的属性并不和数据表的字段一致,这也是ORM灵活的特性之一:支持指定属性获取。

通过gdb.Model.Scan方法可以将查询到的数据记录转换为struct对象或者struct对象数组。由于这里传递的参数为&user**User类型,那么将会转换为一个struct对象,如果传递为[]*User类型的参数,将会转换为数组结果,请查看后续示例。具体方法介绍请查看链式操作章节。

属性字段映射规则:

需要注意的是,map中的键名为uid,name,site,而struct中的属性为Uid,Name,那么他们之间是如何执行映射的呢?主要是以下几点简单的规则:

  1. struct中需要匹配的属性必须为公开属性(首字母大写);
  2. 记录结果中键名会自动按照 不区分大小写忽略-/_/空格符号 的形式与struct属性进行匹配;
  3. 如果匹配成功,那么将键值赋值给属性,如果无法匹配,那么忽略该键值;

以下是几个匹配的示例:

  1. 记录键名 struct属性 是否匹配
  2. name Name match
  3. Email Email match
  4. nickname NickName match
  5. NICKNAME NickName match
  6. Nick-Name NickName match
  7. nick_name NickName match
  8. nick_name Nick_Name match
  9. NickName Nick_Name match
  10. Nick-Name Nick_Name match

由于数据库结果集转struct的底层是依靠gconv.Struct方法实现的,因此如果想要实现自定义的属性转换,以及更详细的映射规则说明,请参考【gconv.Struct】章节。

Result结果集处理

Result/Record数据类型根据数据结果集操作的需要,往往需要根据记录中特定的字段作为键名进行数据检索,因此它包含多个用于转换Map/List的方法,同时也包含了常用数据结构JSON/XML的转换方法。

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

// 数据表记录列表
type Result
    func (r Result) IsEmpty() bool
    func (r Result) Json() string
    func (r Result) List() List
    func (r Result) MapKeyInt(key string) map[int]Map
    func (r Result) MapKeyStr(key string) map[string]Map
    func (r Result) MapKeyUint(key string) map[uint]Map
    func (r Result) RecordKeyInt(key string) map[int]Record
    func (r Result) RecordKeyStr(key string) map[string]Record
    func (r Result) RecordKeyUint(key string) map[uint]Record
    func (r Result) Structs(pointer interface{}) (err error)
    func (r Result) Xml(rootTag ...string) string

由于方法比较简单,这里便不再举例说明。需要注意的是两个方法Record.MapResult.List,这两个方法也是使用比较频繁的方法,用以将ORM查询结果信息转换为可做展示的数据类型。由于结果集字段值底层为[]byte类型,虽然使用了新的Value类型做了封装,并且也提供了数十种常见的类型转换方法(具体请阅读【gvar通用动态变量】章节),但是大多数时候需要直接将结果Result或者Record直接作为json或者xml数据结构返回,就需要做转换才行。

使用示例:

package main

import (
    "database/sql"
    "github.com/gogf/gf/frame/g"
)

type User struct {
    Uid  int
    Name string
    Site string
}

func main() {
    var user []*User
    err := g.DB().Table("user").Where("uid", 1).Scan(&user)
    if err != nil && err != sql.ErrNoRows {
        g.Log().Header(false).Fatal(err)
    }
    if user != nil {
        g.Log().Header(false).Println(user)
    }
}

执行后,输出结果为:

[{"Uid":1,"Name":"john","Site":"https://goframe.org"}]