【最佳实践】gorm 联表查询 joins

这篇具有很好参考价值的文章主要介绍了【最佳实践】gorm 联表查询 joins。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

内容

  • 使用gorm的一些技巧、经验
  • 常规使用示例
  • 如何在一对一、一对多、多对一的关系下使用gorm进行联表查询
    其他gorm使用和进阶用法参考官方文档 https://gorm.io/zh_CN/docs/index.html

优雅表迁移注册

  1. 表自动迁移方式,常见的方式如下:
#model层定义model
package models
type Cluster struct{

}
#global层创建gorm.db对象,并注册迁移表结构
package global
func AutoMigrate() (err error) {
	if err = db.AutoMigrate(
		dbms.Cluster{},
		dbms.Instance{},
		dbms.Account{},
	); err != nil {
		return errors.Wrap(err, "创建数据库表失败")
	}
	return nil
}

这种显示调用方式的缺点是每次新建表结构都要去找global 层的迁移方法并修改,以下推荐一种隐式迁移表结构,通过创建一个迁移全局对象,并把表结构注册分别注册到对象中,最后db.AutoMigrat调用全局变量即可。

#model层定义model和创建全局迁移表对象
package models
var AddMigrate []interface{} 

type Cluster struct{

}

func init() {  // 包初始化后默认注册待迁移表对象
	AddMigrate = append(AddMigrate, Cluster{})
}

#global层创建gorm.db对象,并调用全局迁移表对象
package global
func AutoMigrate() (err error) {
	if err = db.AutoMigrate(model.AddMigrate...); err != nil {
		return errors.Wrap(err, "创建数据库表失败")
	}
	return nil
}

常规操作示例

first、last、take、find区别

  • first会进行排序,主键升序;检索单个对象,LIMIT 1;not found会报错
  • last会进行排序,主键降序;检索单个对象,LIMIT 1;not found会报错
  • take不会进行排序;检索单个对象,LIMIT 1;not found会报错
  • find 获取所有对象;not found 不会报错

查询find

r := s.DB.Model(&models.Cluster{}).
		Where(&models.Cluster{Name: req.Name, EnvCode: req.EnvCode, ClusterType: req.ClusterType}).
		Count(&total).Find(&res)
	if r.Error != nil {
		return nil, 0, errors.Annotate(r.Error, "查询数据库失败")
	}

查询take

err := s.DB.Where(&models.Instance{ID: req.ID}).Take(&ins).Error

create单个记录

if err := s.DB.Create(&models.Cluster{
		Name:          cluster.Name,
		EnvCode:       cluster.EnvCode,
		MasterVipHost: cluster.MasterVipHost,
	}).Error; err != nil {
		return errors.Annotate(err, "创建记录失败")
	}

事务性批量插入

	if err = db.CreateInBatches(list, len(list)).Error; err != nil {
		return errors.Annotate(err, "批量创建账号记录失败")
	}

一次性批量插入

if err = db.Create(list).Error; err != nil {
		return errors.Annotate(err, "批量创建账号记录失败")
	}

delete

	if err = s.DB.Delete(&models.Cluster{ID: id}).Error; err != nil {
		return errors.Annotate(err, "删除记录失败")
	}

查询某个字段

	var total int64
	err := s.DB.Model(&models.Instance{}).Where("cluster_id = ?", id).Count(&total).Error
	if err != nil {
		return errors.Annotate(err, "查询数据库异常")
	}

或者
	var total int64
	err := s.DB.Model(&models.Instance{}).Select("count(*)").Where("cluster_id = ?", id).take(&total).Error
	if err != nil {
		return errors.Annotate(err, "查询数据库异常")
	}

更新某些字段,推荐指定where条件

	if err = s.DB.Where("id = ?",req.ID).Updates(&models.Instance{
		Name:           req.Name,
		InstanceHost:   req.InstanceHost,
		InstancePort:   req.InstancePort,
		ManageUser:     req.ManageUser,
		ManagePassword: req.ManagePassword,
		ClusterRole:    req.ClusterRole,
		Description:    req.Description,
	}).Error; err != nil {
		return errors.Annotate(err, "更新记录失败")
	}

hook简单使用

可以通过gorm提供的hook和context结合,实现变更时自动更新user信息

// 通过controller层传递带有用户信息的context到services层,用gorm.session创建新的db

// controller层保存用户信息到context
context.WithValue(ctx, Cookie, cookie)

//controller层传递ctx
services.NewInstanceService(c.Context()).GetInstanceDetail(id)

//services层
DB:   global.DB.WithContext(ctx),

// 模型注册hook
func (m *Instance) BeforeCreate(tx *gorm.DB) (err error) {
	m.CreateUser = sso.GetNameFromCtx(tx.Statement.Context)
	return nil
}

func (m *Instance) BeforeUpdate(tx *gorm.DB) (err error) {
	m.ModifyUser = sso.GetNameFromCtx(tx.Statement.Context)
	return nil
}

func (m *Instance) BeforeDelete(tx *gorm.DB) (err error) {
	m.ModifyUser = sso.GetNameFromCtx(tx.Statement.Context)
	return nil
}

联表查询实例

以下示例主要实践两个表的关联查询,更多、更复杂的关联查询建议直接使用原生sql进行查询

关联模型定义

表关联,常用使用方式,以下

  • 通过定义表名同名的模型,并指定关联外键,如下
// instance与cluster关联关系 instance belongs to cluster
type Instance struct {
	ID              int    `json:"id" gorm:"primaryKey;autoIncrement;column:id;comment:自增id"`
	Name            string `json:"name" gorm:"type:varchar(64);not null;comment:集群名"`
	InstanceHost    string `json:"instanceHost" gorm:"type:varchar(64);not null;comment:实例连接地址"`
	InstancePort    string `json:"instancePort" gorm:"type:varchar(64);not null;comment:实例连接端口"`
	InstanceVersion string `json:"instanceVersion" gorm:"type:varchar(64);not null;comment:实例版本"`
	ClusterID      int64  `json:"clusterId" gorm:"type:int(11);not null;default:0;comment:关联表cluster"` // 指定关联外键,belongs to关系
	Cluster        Cluster `json:"cluster,omitempty"` // belongs to关系
	Model `json:",omitempty"`
}

type Cluster struct {
	ID            int64  `json:"id" gorm:"primaryKey;autoIncrement;column:id;comment:自增id"`
	Name          string `json:"name" gorm:"type:varchar(64);not null;default:;comment:集群名"`
	EnvCode       string `json:"envCode" gorm:"type:varchar(64);not null;default:;comment:环境code"`
	Model `json:",omitempty"`
}

// 用法 关联查看instance和cluster表
var list []Instance
res := gorm.DB.Joins("Cluster").Where("cluster_id = 1").where(&instance{Name: "test"}).Find(&list) // 注意joins中必须是关联的结构体名,不能是表名
if res.Error != nil {
	return errors.New(err,"查询记录失败")
}
fmt.Println(list)
// select * from dbms_instance LEFT JOIN `dbms_cluster` `Cluster` ON `dbms_instance`.`cluster_id` = `Cluster`.`id` AND `Cluster`.`deleted` = 0 WHERE `dbms_instance`.`name` = "test" AND `dbms_instance`.`deleted` = 0

其他关联关系用法类型

  • 通过手动创建结构体模型承载返回数据,查询时需要指定关联表和关联关系,示例如下
// 模型定义
package models
type Instance struct {
	ID              int    `json:"id" gorm:"primaryKey;autoIncrement;column:id;comment:自增id"`
	Name            string `json:"name" gorm:"type:varchar(64);not null;comment:集群名"`
	InstanceHost    string `json:"instanceHost" gorm:"type:varchar(64);not null;comment:实例连接地址"`
	InstancePort    string `json:"instancePort" gorm:"type:varchar(64);not null;comment:实例连接端口"`
	InstanceVersion string `json:"instanceVersion" gorm:"type:varchar(64);not null;comment:实例版本"`
	ClusterID      int64  `json:"clusterId" gorm:"type:int(11);not null;default:0;comment:关联表cluster"` // 指定关联外键,belongs to关系
	Model `json:",omitempty"`
}

type Cluster struct {
	ID            int64  `json:"id" gorm:"primaryKey;autoIncrement;column:id;comment:自增id"`
	Name          string `json:"name" gorm:"type:varchar(64);not null;default:;comment:集群名"`
	EnvCode       string `json:"envCode" gorm:"type:varchar(64);not null;default:;comment:环境code"`
	Model `json:",omitempty"`
}

// 联合查询 dbms_为表名前缀
type InstanceInfo struct {
		models.Instance
		Cluster models.Cluster
}
selectRes := new(InstanceInfo)
res := s.DB.Model(&models.Instance{}).Select("dbms_instance.*,dbms_cluster.*").Joins("Cluster").Where("dbms_instance.cluster_id = 1").Find(selectRes)
if res.Error != nil {
	return ""
}
fmt.Println(selectRes)
常用关联gorm语句示例

关联关键字:joins、preload

  • joins支持一对一、多对一,生成一条查询sql进行查询
  • preload都支持,分多次查询sql查询,并对结果聚合

对于有where条件情况下的注意事项:

  • where中条件有几种方式

    • string,类似where(“id = ?”,id)或者where("name = @name or name2=@name ",map[string]interface{}{name:“xiaozhuang”})
    • struct,类似where(&Instance{Name:“xiaozhuang”})
    • map,类似where(&map[string]interface{}{“name”: “jinzhu”, “age”: 20})

    推荐使用struct,只有struct可以忽略零值,节省很多判断逻辑

  • 如果where条件中包含的是model/table或者接受结果的模型的表,则无论一对一、多对一、一对多都可以直接使用。

  • 一对多中,如果where条件中包含的是关联的表的条件,则需要preload+join结合使用进行查询 且joins中要手写一层关联关系,否则(只用joins手写关联关系)返回的结果只是进行了筛选,不会赋值到结果结构体中

    todo 例子

示例:

  1. 一对一、多对一
type Cluster struct {
	ID            int64                 `json:"id" gorm:"primaryKey;autoIncrement;column:id;comment:自增id"`
	Name          string                `json:"name" gorm:"type:varchar(64);not null;default:'';comment:集群名"`
	EnvCode       string                `json:"envCode" gorm:"type:varchar(64);not null;default:'';comment:环境code"`
	MasterVipHost string                `json:"masterVipHost" gorm:"type:varchar(128);not null;default:'';comment:集群读写vip地址"`
	Instances     []Instance            `json:"instance,omitempty"`
	Deleted       soft_delete.DeletedAt `json:"-" gorm:"index:idx_deleted"`
	Model         `json:",omitempty"`
}

type Instance struct {
	ID              int64                 `json:"id" gorm:"primaryKey;autoIncrement;column:id;comment:自增id"`
	Name            string                `json:"name" gorm:"type:varchar(64);not null;default:'';comment:集群名"`
	ManagePassword  string                `json:"managePassword" gorm:"type:varchar(256);not null;default:'';comment:管理密码"`
	ClusterID       int64                 `json:"clusterId" gorm:"type:int(11);not null;default:0;comment:关联表cluster"`
	ClusterRole     int                   `json:"clusterRole" gorm:"type:tinyint(4);not null;default:0;comment:在集群中的角色 3:主库 4:从库 5 离线库"`
	Cluster         Cluster               `json:"cluster,omitempty"`
	Deleted         soft_delete.DeletedAt `json:"-" gorm:"index:idx_deleted"`
	Model           `json:",omitempty"`
}

正确示例 where筛选条件只有查询的表有,或者没有条件,此时用joins和preload都可以实现

var list []*models.Instancs
if err := s.DB.Joins("Cluster").Where(&models.Instance{InstanceType: 3}).Find(&list).Error; err != nil {
		return nil, 0, errors.Annotate(err, "查询数据库失败")
}
// select中内容进行截取,方便阅读
 SELECT `dbms_instance`.`id`,`dbms_instance`.`name`,`Cluster`.`id` AS `Cluster__id`, FROM `dbms_instance` LEFT JOIN `dbms_cluster` `Cluster` ON `dbms_instance`.`cluster_id` = `Cluster`.`id` AND `Cluster`.`deleted` = 0 WHERE `dbms_instance`.`instance_type` = 3 AND `dbms_instance`.`deleted` = 0

错误示例 两个表都有where筛选条件条件

可以发现语句只进行了条件筛选,并没有select出结果

 var list []*models.Instance
if err := s.DB.Joins("left join dbms_cluster t1 on t1.id = dbms_instance.cluster_id").
	  Where("t1.env_code = ?","hbos-test").
	 	Where(&models.Instance{InstanceType: 3}).Find(&list).Error; err != nil {
		return nil, 0, errors.Annotate(err, "查询数据库失败")
}
// select中内容进行截取,方便阅读
SELECT `dbms_instance`.`id`,`dbms_instance`.`name`,`dbms_instance`.`instance_host`, FROM `dbms_cluster` left join dbms_instance t1 on t1.cluster_id = dbms_cluster.id WHERE t1.instance_type = 3 AND `dbms_cluster`.`env_code` = 'hbos-test' AND `dbms_cluster`.`deleted` = 0

正确示例 两个表都有where筛选条件条件

可以发现,通过preload语句先进行一次查询,两次查询结果进行聚合可以得到我们想要的多对一的查询结果

 var list []*models.Instance
if err := s.DB.Preload("Cluster").
		Joins("left join dbms_cluster t1 on t1.id = dbms_instance.cluster_id").
	  Where("t1.env_code = ?","hbos-test").
	 	Where(&models.Instance{InstanceType: 3}).Find(&list).Error; err != nil {
		return nil, 0, errors.Annotate(err, "查询数据库失败")
}
// select中内容进行截取,方便阅读
SELECT * FROM `dbms_cluster` WHERE `dbms_cluster`.`id` = 6 AND `dbms_cluster`.`deleted` = 0

 SELECT `dbms_instance`.`id`,`dbms_instance`.`name`,`dbms_instance`.`instance_host` FROM `dbms_instance` left join dbms_cluster t1 on t1.id = dbms_instance.cluster_id WHERE t1.env_code = 'hbos-test' AND `dbms_instance`.`instance_type` = 3 AND `dbms_instance`.`deleted` = 0
  1. 一对多
type Cluster struct {
	ID            int64                 `json:"id" gorm:"primaryKey;autoIncrement;column:id;comment:自增id"`
	Name          string                `json:"name" gorm:"type:varchar(64);not null;default:'';comment:集群名"`
	EnvCode       string                `json:"envCode" gorm:"type:varchar(64);not null;default:'';comment:环境code"`
	MasterVipHost string                `json:"masterVipHost" gorm:"type:varchar(128);not null;default:'';comment:集群读写vip地址"`
	Instances     []Instance            `json:"instance,omitempty"`
	Deleted       soft_delete.DeletedAt `json:"-" gorm:"index:idx_deleted"`
	Model         `json:",omitempty"`
}

type Instance struct {
	ID              int64                 `json:"id" gorm:"primaryKey;autoIncrement;column:id;comment:自增id"`
	Name            string                `json:"name" gorm:"type:varchar(64);not null;default:'';comment:集群名"`
	ManagePassword  string                `json:"managePassword" gorm:"type:varchar(256);not null;default:'';comment:管理密码"`
	ClusterID       int64                 `json:"clusterId" gorm:"type:int(11);not null;default:0;comment:关联表cluster"`
	ClusterRole     int                   `json:"clusterRole" gorm:"type:tinyint(4);not null;default:0;comment:在集群中的角色 3:主库 4:从库 5 离线库"`
	Cluster         Cluster               `json:"cluster,omitempty"`
	Deleted         soft_delete.DeletedAt `json:"-" gorm:"index:idx_deleted"`
	Model           `json:",omitempty"`
}

正确示例 where筛选条件只有查询的表有,或者没有条件,此时只能用preload,用joins会报错

注意:一对多下preload中指定的模型名要加s后缀

	var clusterIns []models.Cluster
	if err := s.DB.Preload("Instances").Where(&models.Cluster{ClusterType: MASTER}).
		Find(&clusterIns).Error; err != nil {
		return nil, 0, errors.Annotate(err, "查询实例所在集群记录失败")
	}

//
SELECT * FROM `dbms_instance` WHERE `dbms_instance`.`cluster_id` = 6 AND `dbms_instance`.`deleted` = 0
SELECT * FROM `dbms_cluster` WHERE `dbms_cluster`.`id` = 6 AND `dbms_cluster`.`deleted` = 0

错误示例 where筛选条件只有查询的表有,或者没有条件,此时只能用preload,用joins会报错

可以发现sql语句出现问题

	var clusterIns []models.Cluster
	if err := s.DB.Preload("Instances").Where(&models.Cluster{ClusterType: MASTER}).
		Find(&clusterIns).Error; err != nil {
		return nil, 0, errors.Annotate(err, "查询实例所在集群记录失败")
	}

//
SELECT `dbms_cluster`.`id`,`dbms_cluster`.`name`,`dbms_cluster`.`env_code` FROM `dbms_cluster` Instance WHERE `dbms_cluster`.`cluster_type` = 3 AND `dbms_cluster`.`deleted` = 0

正确示例 两个表都有where筛选条件条件

var clusterIns []models.Cluster
if err := s.DB.Preload("Instances").
		Joins("left join dbms_instance t1 on t1.cluster_id = dbms_cluster.id").
		Where("t1.instance_type = ?", 3).
		Where(&models.Cluster{ClusterType: 3}).
		Find(&clusterIns).Error; err != nil {
		return nil, 0, errors.Annotate(err, "查询实例所在集群记录失败")
	}

// 		
SELECT * FROM `dbms_instance` WHERE `dbms_instance`.`cluster_id` = 20 AND `dbms_instance`.`deleted` = 0
SELECT `dbms_cluster`.`id`,`dbms_cluster`.`name` FROM `dbms_cluster` left join dbms_instance t1 on t1.cluster_id = dbms_cluster.id WHERE t1.instance_type = 3 AND `dbms_cluster`.`cluster_type` = 3 AND `dbms_cluster`.`deleted` = 0

总结:文章来源地址https://www.toymoban.com/news/detail-459831.html

  • 一对一关系:可直接使用gorm原生的Joins/preload来查询关联表的数据。
  • 多对一关系:多表有where条件要手写一层关联关系,否则直接用preload/Joins
  • 一对多关系: 多表有where条件要手写一层关联关系,否则直接用preload。
  • 多对多关系:有一张关联表,所以需要手写两层关联关系。

到了这里,关于【最佳实践】gorm 联表查询 joins的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 信创办公–基于WPS的EXCEL最佳实践系列 (限制可录入内容)

    信创办公–基于WPS的EXCEL最佳实践系列 (限制可录入内容) 本章内容主要讲解:如何在WPS上利用excel去获取数据,如何通过手工录入数据并完成对录入数据的规范性限制,以及了解一下如何去获取外部数据。 (1)如需对【金额】单元格的录入数据做出规范性限制,则选中该

    2024年02月07日
    浏览(42)
  • 什么是前端安全性(front-end security)?列举一些前端安全性的最佳实践

    聚沙成塔·每天进步一点点 前端入门之旅:探索Web开发的奇妙世界 欢迎来到前端入门之旅!感兴趣的可以订阅本专栏哦!这个专栏是为那些对Web开发感兴趣、刚刚踏入前端领域的朋友们量身打造的。无论你是完全的新手还是有一些基础的开发者,这里都将为你提供一个系统而

    2024年02月05日
    浏览(56)
  • Mysql 复杂查询丨联表查询

    💗wei_shuo的个人主页 💫wei_shuo的学习社区 🌐Hello World ! 联表查询(Join)是一种在数据库中使用多个表进行关联查询的操作。它通过使用 JOIN 将多个表连接在一起,并根据关联条件从这些表中检索数据 INNER JOIN(内连接) 内连接(Inner Join)是一种在联表查询中使用的

    2024年02月13日
    浏览(38)
  • SqlSugar 5.联表查询

    用到的几个实体类 使用 CodeFirst 创建表 1.Join用法 1.1 语法糖1 优点:好理解,五个表以内的联表比较推荐 1.1.1 表和表的连接 若将 .Select((o, cus) = new ViewOrder { Id = o.Id, CustomName =cus.Name }) 修改成.Select((o, cus) = new ViewOrder()) 那就是查询所有字段 1.1.2 表和Queryable的连接 (主表左连了一

    2023年04月08日
    浏览(36)
  • Django 联表查询操作

    在日常的开发中,常常需要对多张数据表同时进行数据查询。多表查询需要在数据表之间建立表关系才能够实现。一对多或一对一的表关系是通过外键实现关联的,而多表查询分为正向查询和反向查询。 以歌手表、专辑表、单曲表查询为例子。 歌手与专辑为一对多关系;歌

    2024年02月07日
    浏览(36)
  • 实时数仓构建:Flink+OLAP查询的一些实践与思考

    今天是一篇架构分享内容。 以Flink为主的计算引擎配合OLAP查询分析引擎组合进而构建实时数仓 ,其技术方案的选择是我们在技术选型过程中最常见的问题之一。也是很多公司和业务支持过程中会实实在在遇到的问题。 很多人一提起实时数仓,就直接大谈特谈Hudi,Flink的流批

    2024年04月15日
    浏览(43)
  • MySQL多表查询内连接外连接详解,join、left join、right join、full join的使用

    目录 1、多表查询概览 1.1、分类 1.2、外连接的分类 1.3、常用的SQL语法标准 2、内外联接案例 2.1、初始化表 2.2、内连接 2.3、外连接案例 2.4、全连接案例 2.5、union和union all 2.6、实现MySQL全连接 2.7、内外连接面试基础 2.8、SQL99多表查询新特性 1.1、分类 可以根据3个角度进行分类

    2024年02月05日
    浏览(65)
  • Elasticsearch 之 join 关联查询及使用场景

    在Elasticsearch这样的分布式系统中执行类似SQL的join连接是代价是比较大的,然而,Elasticsearch却给我们提供了基于水平扩展的两种连接形式 。这句话摘自Elasticsearch官网,从“然而”来看,说明某些场景某些情况下我们还是可以使用的 在关系型数据库中,以MySQL为例,尤其B端类

    2024年02月06日
    浏览(48)
  • MySQL 联表查询重复数据并删除(子查询删除记录) SQL优化

    数据库表介绍: table_a :主表(小表,表数据不可重复) table_b :流水表(大表,记录审核流水数据) 注:两表表结构大致一致,流水表增加一个审核状态的字段 业务逻辑: 主表保存唯一数据,流水表记录审核流水数据,用于后续展示,并在审核成功后插入主表,在插入流

    2023年04月08日
    浏览(65)
  • JPA 如何修改 联表查询返回的Map

    记录解决两个问题: 解决方法: 不直接修改这个 Map。如果你需要对 Map 进行修改操作,你可以创建一个新的 Map,然后将原 Map 的内容复制到新 Map 中。  第二个问题就是如何创建一个空的PageMap 把数据拷贝进去了 参数描述:    创建新对象并赋值伪代码如下:

    2024年04月27日
    浏览(30)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包