Gorm 预加载及实现联表条件查询仿WhereHas
Posted vwvwvwgwgvervae
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Gorm 预加载及实现联表条件查询仿WhereHas相关的知识,希望对你有一定的参考价值。
文献参考 http://gorm.book.jasperxu.com/
写Go代码也有快一个月了,最近在将laravel项目转Gin的过程中,遇到了不少因为语法特性而导致迁移问题,其中一个就是Gorm这块
With方法被 Preload ,Association 替代
在laravel中,我们可以通过with方法将关联模型的数据引入并合并到查询的数据结构中
常见的写法如
$builder = Dynamic::query()->with([ ‘user:id,nickname,avatar,gender‘, ‘detail:id,dynamic_id,media_url,cover_img_url,media_time_length‘, //‘stat:id,dynamic_id,share_number,like_number,comment_number‘, ‘topic:id,topic_name,topic_type‘ ])->where(function($query) use($topicList,$followList){ $query->whereIn("topic_id", $topicList) ->orWhereIn(‘user_id‘,$followList); });
Gorm的Preload和Association方法的作用也是类似于此,主要的区别在于Association服务于主体,Preload可以服务于关联关系的模型
db.Model(&user).Association("Languages").Find(&languages)
db.Preload("Orders").Preload("Profile").Preload("Role").Find(&users)
//// SELECT * FROM users;
//// SELECT * FROM orders WHERE user_id IN (1,2,3,4); // has many
//// SELECT * FROM profiles WHERE user_id IN (1,2,3,4); // has one
//// SELECT * FROM roles WHERE id IN (4,5,6); // belongs t
不是很清楚的朋友可以直接跳官方文档查询,实际场景中大家根据需要自行调用即可
本人的理解设计两个方法的原因主要在于,Go的语法中不能存在循环引用
比如我在模型Order中关联了User ,那么我们就无法在User 中关联Order了,因为这样触发了循环引用的原则
但是通过 Preload 和 Association 就能实现主体反转,从而解决了循环嵌套的问题
Gorm中无WhereHas方法
在官方的文档中,我们可以实现模型的 一对一,一对多,多对多的关联
但是却没有在关联关系中添加条件的方法
如果查询条件中只需要一个关联表的数据,实现还是可以比较优雅
//查询当前用户关注的动态列表,并做分页,预加载User,Topic模型的处理 database.DB.Model(brief).Offset(offset).Limit(limit).Where("status = ? and delete_status = ?",dynamic.STATUS_ABLE,dynamic.DELEET_STATUS_DEFAULT).Preload("User").Preload("Topic").Preload("AppTopicDynamicDetail").Related(&list, "follow_dynamic").Find(&list)
但是实际场景中,会有更复杂的情况 , 我除了要查询用户关注的动态,还是把用户关注的话题相关的动态一并查出来
这里可以有两种方案
第一种 ,gorm也支持where条件的嵌套应用,这样使得我们在控制OR条件的时候能有效的控制闭合关系,
但是我们要处理WhereHas的问题还是要用到 原生的where exists,嵌套where的作用并没有很大,我们直接用原生sql ,查询条件的字段中要把表名带上,直接上代码
//关注的动态和关注话题相关的动态都要显示 database.DB.Model(dyn).Offset(offset).Limit(limit) .Where("status = ? and delete_status = ? AND (EXISTS(SELECT * FROM app_dynamic_follow WHERE `app_dynamic_follow`.`user_id` IN (?) AND app_topic_dynamic.id = app_dynamic_follow.`dynamic_id`) OR topic_id IN (?)) ", 1, 0 , brief.ID,strings.Join(topicIds,",") ).Preload("User").Preload("Topic").Preload("AppTopicDynamicDetail").Find(&list)
这里也算是把laravel中whereHas的sql翻译过来
Explain执行效率,用上了user_id的索引
第二种,Join 方法,但是灵活性和局限性就没那么高 ,并且也算是脱离了ORM的概念了,基本也是接近引用原生sql了,
database.DB.Table("app_topic_dynamic").Offset(offset).Limit(limit).Where("status = ? and delete_status = ?",dynamic.STATUS_ABLE,dynamic.DELEET_STATUS_DEFAULT).Joins("left join app_dynamic_follow on app_topic_dynamic.id = app_dynamic_follow.`dynamic_id` and (app_topic_dynamic.user_id = ? OR topic_id IN (?))",brief.ID,strings.Join(topicIds,",")).Preload("User").Preload("Topic").Preload("AppTopicDynamicDetail").Find(&list)
Explain执行效率,并没有应用索引原因是 Using where; Using join buffer (Block Nested Loop),基本上是最慢的
综合所上,,这里更推荐大家用第一种方法,也就是laravel中WhereHas的sql语法 where exists
如有疑惑和描述不准确,欢迎大家给我留言
关键词:go , golang , gin , gorm ,laravel
来源:抚州SEO(优化推广)
以上是关于Gorm 预加载及实现联表条件查询仿WhereHas的主要内容,如果未能解决你的问题,请参考以下文章