前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >学习gorm系列五:gorm中的核心数据结构

学习gorm系列五:gorm中的核心数据结构

作者头像
Go学堂
发布2023-11-01 18:24:49
4090
发布2023-11-01 18:24:49
举报
文章被收录于专栏:Go工具箱

大家好,我是渔夫子。

今天咱们一起来学习下gorm中的几个核心数据结构。通过了解gorm底层的数据结构,能够让我们了解gorm底层的实现,以便更好的使用gorm。

在gorm中主要有5个核心结构:DB、Config、Statment、Clause和Schema。接下来我们就详细的看下每种数据结构以及各结构之间的关系。

一、DB

在使用gorm的时候,我们首先会使用gorm.Open方法和数据库建立连接,同时并返回一个gorm.DB结构。如下:

代码语言:javascript
复制
var db *gorm.DB
dsn := "username:password@tcp(127.0.0.1:3306)/test01?charset=utf8mb4&parseTime=True&loc=Local&timeout=1000ms"

config := &gorm.Config{
 NamingStrategy: schema.NamingStrategy{
     SingularTable: true, // 禁用表名复数
 },
}

db, _ := gorm.Open(mysql.Open(dsn), config)

如上,db变量就是*gorm.DB类型的,如下:

代码语言:javascript
复制
// DB GORM DB definition
type DB struct {
 *Config
 Error        error
 RowsAffected int64
 Statement    *Statement
 clone        int
}

大家看,DB结构里包含了嵌入的结构*Config,还有一个Statement结构。

二、Config结构

Config结构是包含在DB结构内的。顾名思义,Config就是和数据库相关的一些配置。在gorm.Open函数中传入的,如上面我们对数据表命名的配置中禁用了表名的复数形式。如下:

代码语言:javascript
复制
config := &gorm.Config{
 NamingStrategy: schema.NamingStrategy{
     SingularTable: true, // 禁用表名复数
 },
}

这个的作用就是我们在建立和表对应的Model结构体时,结构体的名称会转换成对应的表名,但使用的是单数形式。例如,我们有一个MTest的Model结构体。那么,默认情况下,gorm会把该model转换成对应的m_tests表名。而如果做了对应的配置:SingularTable:True,gorm就会把该model转换成对应的m_test表名。

以下就是Config结构中的核心字段(我们省略了一些):

代码语言:javascript
复制
// Config GORM config
type Config struct {
 // NamingStrategy tables, columns naming strategy
 NamingStrategy schema.Namer
 // DryRun generate sql without execute
 DryRun bool
 // ConnPool db conn pool
 ConnPool ConnPool
 // Dialector database dialector
 Dialector
 // Plugins registered plugins
 Plugins map[string]Plugin

 callbacks  *callbacks
}

在该结构中,各字段如下:

  • ConnPool:是和数据库建立的真实连接。
  • Dialector:是连接器。这个是一个接口,以适配各种类型的数据库。比如MySQL、ClickHouse等。比如,在最开始传入Open函数的第一个参数mysql.Open(dsn)就是一个Dialector,说明要连接的是mysql数据库。后续所有操作都是针对mysql数据库的。
  • callbacks:callbacks是一个结构体,该结构体包含了一个map结构的processors的字段。该processors是实际的执行器。会有4个对应的processor,分别为:CREATE、QUERY、DELETE和UPDATE。用于分别执行对应的语句。
  • DryRun:该参数是一个是否执行最终sql语句的一个开关。如果为false,则执行sql语句,否则只将对应的函数编译成sql语句,但不实际执行。

三、Statement

statement代表的是语句。这里就包含了sql种涉及到的所有语句了。其结构如下:

代码语言:javascript
复制
type Statement struct {
 *DB
 TableExpr            *clause.Expr //表表达式
 Table                string //表名
 Model                interface{} //model结构体
 Dest                 interface{} // 接收查询结果的变量
 ReflectValue         reflect.Value //model类型的value值
 Clauses              map[string]clause.Clause //sql语句
 BuildClauses         []string
 Distinct             bool //是否去重
 Selects              []string // 要查询的字段列表
 Omits                []string // 忽略的字段 
 Joins                []join // join的表
 Preloads             map[string][]interface{}
 Settings             sync.Map
 ConnPool             ConnPool
 Schema               *schema.Schema // 对应的表的模式
 Context              context.Context
 SQL                  strings.Builder //最终编译好的sql语句
 Vars                 []interface{} // 从句中涉及到的变量值
}

这里只列出了一些关键的字段。我们分类讲解一些这些关键字段。

表相关字段

TableExpr和Table:这两个字段都是通过DB.Table(name string, args ...interface{})函数指定的。比如我们常用的DB.Table("m_test")就可以指定要查询的表名。

Model和Schema字段

通过model字段可以指定和数据表对应的结构体类型。然后gorm再通过model结构体转换成对应的表的建表模式,并将其复制到Schema字段中。

Dest字段

Dest字段用来接收从数据表中查询的结果。我们看Dest的类型是interface{},也就是说可以是任意类型。Dest一般是通过Find函数、Save、Create等函数传进来的。如下各函数的原型:

代码语言:javascript
复制
// dest会复制给Dest字段
func (db *DB) Find(dest interface{}, conds ...interface{}) (tx *DB)

//  会将value参数赋值给Dest字段
func (db *DB) Save(value interface{}) (tx *DB)

//  会将value参数赋值给Dest字段
func (db *DB) Create(value interface{}) (tx *DB)

同时,这里还会有一个特点,就是当Model字段为nil时,会将Dest字段赋值给Model字段。这也就是为什么我们在使用gorm的时候,给Find函数传一个Model类型的dest就能定位到对应的表,并将数据获取出来。而给Find函数传递一个map类型的dest,就必须要通过Table函数指定一个表名的原因。如下:

代码语言:javascript
复制
// 定义一个MTest的model
type MTest struct {
 Id   int64
 Name string `gorm:"DEFAULT:John"`
}

// 定义一个model变量
var row MTest

// 未指定表,依然能从m_test表中查找到数据
err := db.Find(&row)


// 示例二
// 定义一个MTest的model
type MTest struct {
 Id   int64
 Name string `gorm:"DEFAULT:John"`
}

// 定义一个map类型用来接收数据
var row = make(map[string]interface{},0)

// 需要指定model,才能从m_test表中查找到数据
err := db.Model(MTest{}).Find(&row)

四、Schema

Schema字段是通过解析Model字段而得到的值,和数据表的模式对应。一个数据表的模式包含表名、字段及字段默认值、主键字段。以下是Schema结构体的一些重点字段:

代码语言:javascript
复制

type Schema struct {
 Name                      string //model的名称
 ModelType                 reflect.Type
 Table                     string //表名
 PrioritizedPrimaryField   *Field //最优先的主键ID
 DBNames                   []string //通过在model中指定tag(COLUMN)来和数据表中的字段关联
 PrimaryFields             []*Field //主键字段集合
 PrimaryFieldDBNames       []string //主键字段集合(数据库中的字段名)
 Fields                    []*Field //结构体中的字段名、数据库中的字段名
 FieldsByName              map[string]*Field //以结构体中的字段名为key的map
 FieldsByBindName          map[string]*Field // embedded fields is 'Embed.Field' 嵌入的字段名称
 FieldsByDBName            map[string]*Field // 以数据库中的字段名为key的map
 FieldsWithDefaultDBValue  []*Field // fields with default value assigned by database 有默认值的字段
 Relationships             Relationships //相关联的表
}

在该结构体中,最核心的字段就是Fields字段。该Fields字段就是从对应的Model结构体中通过reflect解析出来的字段。该字段默认是跟数据表中的字段一一对应的。

五、Clause

在sql语句中,各个关键词对应的就是从句,即Clause。比如Select、From、Where、Order、Group By、Limit等等。在gorm中,会通过对应名称的函数来组织对应的从句。比如Where从句:

代码语言:javascript
复制
// 组织成Where从句
func (db *DB) Where(query interface{}, args ...interface{}) (tx *DB)

// 组织成GroupBy从句
func (db *DB) Group(name string) (tx *DB)

比如以下就是指定了Where的从句:

代码语言:javascript
复制
type MTest struct {
 Id   int64
 Name string `gorm:"DEFAULT:John"`
}

var row MTest

err := db.Where("id=?", 1).Find(&row)

在gorm中,将各个从句定义成了对应的类型。同时,这些类型又实现了Clause接口类型。如下是Clause接口的定义:

代码语言:javascript
复制
// Interface clause interface
type Interface interface {
 Name() string
 Build(Builder)
 MergeClause(*Clause)
}

在这个接口中,有一个Build函数需要额外注意。各个具体的从句就是通过Build这个函数来转换成对应的sql语句的。比如Where从句:

代码语言:javascript
复制
// Build build where clause
func (where Where) Build(builder Builder) {
 // Switch position if the first query expression is a single Or condition
 for idx, expr := range where.Exprs {
  if v, ok := expr.(OrConditions); !ok || len(v.Exprs) > 1 {
   if idx != 0 {
    where.Exprs[0], where.Exprs[idx] = where.Exprs[idx], where.Exprs[0]
   }
   break
  }
 }

 buildExprs(where.Exprs, builder, AndWithSpace)
}
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2023-10-30,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Go学堂 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、DB
  • 二、Config结构
  • 三、Statement
    • 表相关字段
      • Model和Schema字段
        • Dest字段
        • 四、Schema
        • 五、Clause
        相关产品与服务
        数据库
        云数据库为企业提供了完善的关系型数据库、非关系型数据库、分析型数据库和数据库生态工具。您可以通过产品选择和组合搭建,轻松实现高可靠、高可用性、高性能等数据库需求。云数据库服务也可大幅减少您的运维工作量,更专注于业务发展,让企业一站式享受数据上云及分布式架构的技术红利!
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档