GoooQo是一款动态查询语言的Golang引擎,支持通过对象直接构建查询语句,特别是通过查询对象动态构建查询语句。它通过反射技术读取查询字段的名称和赋值,实现动态生成查询条件,从而简化开发工作。
为了优化性能,GoooQo 推出了代码生成工具 gooogen。该工具通过抽象语法树(AST)技术读取查询字段名称,自动生成查询语句构建方法,提供了一种替代反射技术构建动态查询的方法。
反射技术
在GoooQo中,开发者只需定义一个实体对象和一个包含有查询字段的查询对象,即可完成单表的增删查改操作。以查询对象UserQuery为例,该结构体包含了多个可选的查询条件字段:
type UserQuery struct {
PageQuery
Name *string
NameContain *string
AgeGt *int
AgeLt *int
UserOr *UserQuery
AgeLtAvg *UserQuery `select:"avg(age)" from:"t_user"`
}
谓词后缀字段的名称由属性和谓词组成,通过反射技术读取字段名称后即可直接映射为SQL查询条件,如NameContain
表示name LIKE ?
,AgeGt
表示age > ?
。
逻辑后缀字段的名称包括逻辑连接词And
或Or
,用于指定多个
查询条件的连接方式,如UserOr
。
子查询字段通过名称构建基础条件,通过标签和赋值构建子查询条件,如AgeLtAvg
。
GoooQo通过反射技术为每个字段构建查询条件,并根据字段赋值确定组合的查询条件。但是这也带来了一定的性能问题。
代码生成技术
为了消除反射引入的性能问题,GoooQo推出了一款代码生成工具gooogen,可以自动为用户定义的查询对象生成对应的动态查询构建代码,大大提高了代码的编写效率。
首先,通过下面这条命令安装gooogen:
go install github.com/doytowin/goooqo/gooogen@latest
然后,在查询对象的定义上添加注释:
//go:generate gooogen -type sql -o user_query_builder.go
type UserQuery struct {
//...
}
最后,执行go generate
命令便会生成一个user_query_builder.go
文件,其中包含了根据UserQuery
结构体自动构建SQL查询条件的方法:
func (q UserQuery) BuildConditions() ([]string, []any) {
conditions := make([]string, 0, 4)
args := make([]any, 0, 4)
if q.Name != nil {
conditions = append(conditions, "name = ?")
args = append(args, *q.Name)
}
if q.NameContain != nil {
conditions = append(conditions, "name LIKE ?")
args = append(args, "%"+*q.NameContain+"%")
}
if q.AgeGt != nil {
conditions = append(conditions, "age > ?")
args = append(args, *q.AgeGt)
}
if q.AgeLt != nil {
conditions = append(conditions, "age < ?")
args = append(args, *q.AgeLt)
}
if q.UserOr != nil {
cond, args0 := BuildConditions(q.UserOr, "(", " OR ", ")")
conditions = append(conditions, cond)
args = append(args, args0...)
}
if q.AgeLtAvg != nil {
where, args0 := BuildConditions(q.AgeLtAvg, " WHERE ", " AND ", "")
conditions = append(conditions, "age < (SELECT avg(age) FROM t_user"+where+")")
args = append(args, args0...)
}
return conditions, args
}
这段代码会根据UserQuery对象的赋值构建一组查询条件以及对应的参数,通过WHERE和AND连接便能得到对应的查询子句,进而构建完整的查询语句并执行。
query := UserQuery{Name: P("Tim"), UserOr: &UserQuery{AgeGt: P(10), AgeLt: P(20)}}
where, args := rdb.BuildWhereClause(query)
// where: WHERE name = ? AND (age > ? OR age < ?)
query := UserQuery{AgeGt: P(10), AgeLtAvg: &UserQuery{NameContain: P("J")}}
where, args := rdb.BuildWhereClause(query)
// where: WHERE age > ? AND age < (SELECT avg(age) FROM t_user WHERE name LIKE ?)
通过对比UserQuery
对象和生成的BuildConditions
方法可知,
在UserQuery
对象中每添加一个字段,就需要在BuildConditions
方法中对应添加4行代码用于查询条件的拼接,代码量对比为1比4,生成的代码数量占了80%。再加上围绕BuildConditions
方法封装的代码,GoooQo可以帮助减少至少80%以上的数据库访问代码。
总结
GoooQo的代码生成工具gooogen
通过自动化生成动态查询构建代码,有效替代了反射技术,既保证了高效运行性能,又大幅提升了开发效率。开发者只需定义查询对象,便可自动生成复杂的查询条件构建器,在保证性能的同时,大幅提升开发效率。