持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第2天,点击查看活动详情
一、问题描述
前端界面使用了vuetify组件,在编写「文章列表」部分时,发现文章不能按照设定的pagesize和pagenum展示指定的部分数据,而是一股脑展示了全部数据。
二、问题分析
1.后台「文章列表」页面正确展示:
后台「文章列表」页面和出问题的前台页面调用的是同一个API函数。
2.后台「文章列表」页面和UI的请求参数是一致的:
在 Go 后端的「查询文章列表」API函数中埋入print(),
每次调用时打印pagesize和pagenum。
从前端请求接收到的pagesize和pagenum将会传给Model层。
- 在后台「文章列表」点击分页器
第2页和第1页:
- 在前端点击「文章列表」组件的
第2页和第1页:
3.寻找问题根源
(1) Bug 不在后端,应该是前端`vuetify`的使用出错。
(2) Bug 在后端,但是后台组件`Ant Design Vue`对这个问题进行了容错设计。
使用API工具向后端发送模拟请求,发现不管Query的pagesize和pagenum设为多少,都返回全部数据。
三、※ 问题解决
修改后端的Model相关函数:
将Limit()和Offset()挪到Find()和Preload()前,问题解决:
另外,由于
文章 belongs to 分类,属于一对一关系
将原有 Preload() 改为 Joins()单次查询以提高性能。
使用 API 工具,响应无误:
四、总结与拓展
Bug的出现是因为对GORM的语句链次序不熟悉。
1.关于Gorm的Limit和Offset在语句链中的位置问题。
Limit和Offset总要放在Find()前面
2. 关于Gorm的Preload和Joins的区别(Joins只是一种特别的Preload):
(1)底层执行的SQL原理不同
Preload 在一个单独查询中加载关联数据,也就是说,当每使用一个Preload('tableName'),底层对 SQL 就新增一条查询语句:
db.Find(&users)
// SELECT * FROM users;
=======3个 Preload() 新增 3 次预加载语句=======
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 to
而 Joins 会使用 left join 加载关联数据,可以在 1 条语句中完成。
db.Joins("Company", DB.Where(&Company{Alive: true})).Find(&users)
// SELECT * FROM `users` LEFT JOIN `companies` ON `users`.`company_id` = `Company`.`id` AND `Company`.`alive` = true;
(2)表关系的适用范围不同
Preload适用于多表的各种关系,包括has one、has many、belongs to等。
Joins只适用于1:1关系,如has one和belongs to关系。
相关阅读: 预加载 | GORM - The fantastic ORM library for Golang, aims to be developer friendly.