golang gorm学习之如何指定数据表

 更新时间:2023年08月24日 09:07:07   作者:Go学堂  
在sql中首先要指定是从哪张表中查询,所以这篇文章小编就来带大家一起看一下gorm是如何根据model来自动解析表名的,感兴趣的小伙伴可以了解下

一、通过model名称自动匹配表名

我们看如下例子:

type MTest struct {
	Id int64
	Name string
}
func main() {
	dsn := "test:123456@tcp(localhlost:3306)/test01?charset=utf8mb4&parseTime=True&loc=Local&timeout=1000ms"
	db, _ := gorm.Open(mysql.Open(dsn))
	m := MTest{}
	db.Find(m)
	fmt.Println(m)
	fmt.Println(db.Statement.Table)
}

在这个例子中,表名是通过根据model的结构体的名字MTest来指定的。规则如下:

  • 若是驼峰的形式,则两个大小字母之间会加一个下划线连接。
  • 是结构体名称的复数形式。复数形式符合英文单词的复数规则。例如若最后一个单词是s,那么最终表名就不会再加s。比如model结构体的名称是a01s,那么最终的表名就是a01s。

所以,上面的表名最终是:m_tests

这种方式的实现,本质上是通过gorm.Open中指定的名称策略来实现的。如下:

在NamingStrategy结构体中,可以指定前缀以及是否是复数形式,如下:

func (ns NamingStrategy) TableName(str string) string {
	if ns.SingularTable {
		return ns.TablePrefix + ns.toDBName(str)
	}
	return ns.TablePrefix + inflection.Plural(ns.toDBName(str))
}

指定表前缀、禁用复数

通过在gorm.Open的函数中指定对应的配置选项,就可以给本次连接做相关的配置。比如指定表名前缀,禁用表名复数等。如下:

type MTest struct {
	Id int64
	Name string
}
func main() {
	dsn := "sands:123456@tcp(test.trdplace.ads.sg1.mysql:3306)/test01?charset=utf8mb4&parseTime=True&loc=Local&timeout=1000ms"
	config := &gorm.Config{
		NamingStrategy: schema.NamingStrategy{
			TablePrefix:   "pre_",   // 表前缀
			SingularTable: true, // 禁用表名复数
		}}
	db, _ := gorm.Open(mysql.Open(dsn), config)
	m := MTest{}
	db.Find(m)
	fmt.Println(m)
	fmt.Println(db.Statement.Table)
}

如上,最终打印出的表名就是m_test

二、通过实现Tabler接口指定表名

第二种方式是通过让model结构体实现TableName() string 函数来指定具体的表名。如下:

type MTest struct {
	Id int64
	Name string
}
func (m *MTest) TableName() string {
    return "m_test"
}

这样,表名就是TableName指定的字符串,指定什么就是什么。其实这个本质上是实现了Tabler接口:

type Tabler interface {
	TableName() string
}

三、通过实现TablerWithNamer接口指定表名

第三种方式是让Model结构体实现TablerWithNamer接口来指定表名。接口定义如下:

type TablerWithNamer interface {
	TableName(Namer) string
}

这里的Namer也是一个接口,在第一种方式中,实际上就是指定了一个Namer,在gorm.Open函数中,我们看下:

当然,我们也可以自己实现Namer接口来指定具体的表名规则。

四、通过embeddedNamer结构体

这个是在Namer接口的基础上,如果该Namer是embeddedNamer结构体类型,那么优先使用这个结构体中指定的名称。embeddedNamer结构体如下:

type embeddedNamer struct {
	Table string
	Namer
}

五、查询时直接通过db.Table函数指定表名

使用db.Table指定的表后,gorm将不再使用自动解析出来的表名了。所以该方式的优先级是最高的。如下:

type MTest struct {
	Id int64
	Name string
}
func main() {
	dsn := "123456:123456@tcp(localhost:3306)/test01?charset=utf8mb4&parseTime=True&loc=Local&timeout=1000ms"
	db, _ := gorm.Open(mysql.Open(dsn))
	m := MTest{}
	db.Table("m_tests").Find(m)
}

六、实现原理

还是以db.Find(m)为例。 首先,Find函数会把m对象赋值给db.Statement.Dest字段。这个Dest字段就是要查询的目标数据。然后再调用db.callbacks.Query().Execute(tx)。就是实际的查询函数。如下是db.Find的代码:

func (db *DB) Find(dest interface{}, conds ...interface{}) (tx *DB) {
	tx = db.getInstance()
	if len(conds) > 0 {
		if exprs := tx.Statement.BuildCondition(conds[0], conds[1:]...); len(exprs) > 0 {
			tx.Statement.AddClause(clause.Where{Exprs: exprs})
		}
	}
	tx.Statement.Dest = dest
	return tx.callbacks.Query().Execute(tx)
}

其中tx是从原db中再获取一个db对象,同时也具有了对应的数据库连接的对象。然后tx.callbacks.Query()其实就是db.Open函数中初始化的callbacks:db.callbacks = initializeCallbacks(db)。如下:

func initializeCallbacks(db *DB) *callbacks {
	return &callbacks{
		processors: map[string]*processor{
			"create": {db: db},
			"query":  {db: db},
			"update": {db: db},
			"delete": {db: db},
			"row":    {db: db},
			"raw":    {db: db},
		},
	}
}

其次,在processor.Execute函数中会看到,statement.Model和statement.Dest会相互赋值。如下:

最主要的是在statement.Parse函数解析的表名了。依次看代码调用,最终会到schema.go文件中的ParseWithSpecialTableName函数中,在这个函数中,有对表名的解析。如下:

七、总结

本文总结了gorm中如何指定表名的多种方式。其中优先级最高的是通过gorm会根据指定的model结构体的名称自动解析出表名。同时,如果model实现了Tabler接口或TablerWithNamer接口,那么就会优先根据这两个接口的对应TableName函数指定的表名进行解析。

以上就是golang gorm学习之如何指定数据表的详细内容,更多关于gorm指定数据表的资料请关注脚本之家其它相关文章!

相关文章

  • Go语言开发必知的一个内存模型细节

    Go语言开发必知的一个内存模型细节

    这篇文章主要为大家介绍了Go语言开发必知的一个内存模型细节详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-07-07
  • 使用Go语言编译Linux平台可执行文件的完整指南

    使用Go语言编译Linux平台可执行文件的完整指南

    在Go语言开发中,跨平台编译是一个非常重要的功能,本文将详细介绍如何使用Go语言将项目编译为Linux平台的可执行文件,特别是针对国内开发者的优化配置,需要的朋友可以参考下
    2025-08-08
  • GoZero实现数据库MySQL单例模式连接的简单示例

    GoZero实现数据库MySQL单例模式连接的简单示例

    在 GoZero 框架中实现数据库的单例连接可以通过以下步骤来完成,GoZero 使用 gorm 作为默认的数据库操作框架,接下来我会展示一个简单的单例模式实现,需要的朋友可以参考下
    2025-02-02
  • Go 使用Unmarshal将json赋给struct出错的原因及解决

    Go 使用Unmarshal将json赋给struct出错的原因及解决

    这篇文章主要介绍了Go 使用Unmarshal将json赋给struct出错的原因及解决方案,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2021-03-03
  • Go语言开发保证并发安全实例详解

    Go语言开发保证并发安全实例详解

    这篇文章主要为大家介绍了Go语言开发保证并发安全实例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-09-09
  • Go语言实现动态解析JSON数据的多种方式

    Go语言实现动态解析JSON数据的多种方式

    本文主要介绍了Go语言实现动态解析JSON数据的多种方式,包括map[string]interface{}、interface{}、json.RawMessage及第三方库gjson/mapstructure,具有一定的参考价值,感兴趣的可以了解一下
    2025-05-05
  • Go语言学习之运算符使用详解

    Go语言学习之运算符使用详解

    这篇文章主要介绍了Go语言中常用运算符的使用,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2022-04-04
  • 一文带你探索Go语言中的函数一等公民

    一文带你探索Go语言中的函数一等公民

    你是否听说过 Go 语言中的函数是一等公民,如果没有,那么恭喜你,本文将带你一起揭开这个神秘的面纱,感兴趣的小伙伴快来和小编一起学习起来吧
    2023-07-07
  • go mod tidy报错解决方法详解

    go mod tidy报错解决方法详解

    这篇文章主要为大家介绍了go mod tidy报错解决方法详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-12-12
  • Go语言性能监控和调优的工具和方法

    Go语言性能监控和调优的工具和方法

    本文介绍了Go语言性能监控和调优的工具和方法,包括 pprof、expvar 和 trace 等工具的使用方法和注意事项,以及性能调优的一些常见方法,如减少内存分配、避免频繁的垃圾回收、避免过度查询数据库等,针对不同的程序,应该根据实际情况采用不同的优化方法
    2024-01-01

最新评论