一种使用GORM实现动态查询的可行方案
前言
前段时间,因工作需要,需要利用Go中GORM(Go中一个比较优秀的ORM框架)获取MySQL数据库中的一些数据,并以分页的方式展示。格式化需求主要是以下两条
- 可以根据用户权限显示不同的数据
- 存在可选的条件过滤方式动态获得数据
其中关键是能否根据不同的条件后构成了具有不同查询功能的SQL语句,同样隐性包含了一个小需求:避免SQL注入
^ 1。经过一番查验与尝试之后,效率较低地实现了这种功能,如有必要,日后继续进行SQL语句的优化。写在此刻,是为了日后校验。
动态拼接
无用的废话:之所以需要动态拼接,一个很重要的原因就是一个SQL语句的输入只会对应一种输出。所有动态拼接的核心其实就是根据前端所传入的不同选择条件以及字段,判断后并得出符合此类输入条件的SQL语句。
而GORM中,是可以支持链式操作^ 2的。链式操作可以通过传入同一个对象,从而获取此对象之前存在的值,并在其后添加另外的规则,从而实现拼接SQL语句。并且Gorm本身已经对提供了一些阻止SQL注入的规则,不需要自己重新去设置阻止SQL注入的规则。
具体示例
因工作原因,无法显示项目中的实时数据。
输入
现在存在三张表,三张表的结构如下所示。现在存在核心需求,可以根据使用者(老师)权限显示目前的Student人数。需要添加的需求有如下几种
- 老师可以根据选择性别查询(可选)
- 老师可以根据学生姓名查询(可选)
- 老师可以根据学生电话查询(可选)
- 以分页的形式呈现数据(必选),分页的大小和具体的页数(可选)
type Student struct {
ID int `gorm:"primary_key"`
Phone int
Name string
Age int
Sex string
Address string
RoleID int
}
type Teacher struct {
ID int `gorm:"primary_key"`
Name string
}
type Role struct {
ID int `gorm:"primary_key"`
StudentID int // 对应Student中的RoleID
TeacherID int
}
以上三张表中核心内容是在Role表中实现每个老师可以获取权限以内的学生数据。通过遍历Role表,获取单个老师拥有的权限集合\(S\)。值得一提的是,实际项目中判断并获取这种集合\(S\),获取后的集合\(S\)会插入Redis中,便于下次快速查询此集合,减少数据库的负载;另外,每次修改权限后,会重新往redis中更新对应的权限集合\(S\),实现同步。以上是实现项目高可用的一种方式。
处理
通过\(S\)就可以判断可以获取权限的用户数据了,而整合各项需求后可以使用Gorm框架如此实现。
var db *gorm.DB // 获取已经初始化的Gorm.DB对象
func GetUserByRole(role []string, sex, name string, phone, page, pageSize int) (student []Student, err error) {
DB := db // 拷贝一个全局Gorm.DB对象
DB.Table("user") // 指定目标表
if name != "" {
DB = DB.Where("name like ?", "%"+name+"%") // 开始链接
}
if phone > 0 {
DB = DB.Where("phone = ?")
}
if sex != "" {
DB = DB.Where("sex = ?", sex)
}
if role != nil {
DB.Where("role in ?", role)
}
err = DB.Order("name").Limit(pageSize).Offset(page).Find(&student).Error
return
}
注意,目前由于数据量不是太大,使用In进行权限判断的延迟是可以接受的。
简短总结
从实际上看,这个分页实现地比较简单,一方面看,只是是对SQL语句的拼写以及对GORM的简单使用而已。从另外一方面看,目前项目中存在数据量不超过10000条,性能是可以满足大致需求的。所以再次仅仅做一个记录,便于查询。