go gorm 判断 db 对象是否存在 where 条件
在使用 GO 语言的 GORM 框架进行数据库开发时,我们经常会遇到需要判断当前构建的 DB 对象是否已经添加了 WHERE 条件的场景。
比如在封装通用查询函数时,要避免重复添加默认条件,或者根据是否有自定义条件来决定是否执行某些逻辑。
通过对 go gorm 源码的解读 Go 源码之 gorm 框架, 掌握了几种实用的判断方法,记录分享下,方便大家直接套用。
核心思路
核心思路:解析 GORM 的 Statement 结构体
GORM 的 DB 对象内部包含一个 Statement 结构体,这个结构体记录了当前查询的所有关键信息,包括 WHERE 条件、表名、字段等。
我们判断是否存在 WHERE 条件,本质上就是解析 Statement 中的相关属性。
在 GORM v2 版本中,Statement 结构体的 WhereClauses 字段专门存储 WHERE 条件列表。
这个列表的长度是否大于 0,是判断是否存在 WHERE 条件的关键依据。
不过需要注意的是,有些场景下会自动添加默认条件(比如软删除的 deleted_at IS NULL),我们需要根据实际需求决定是否排除这类默认条件。
方法一:直接判断 WhereClauses 长度
这种方法最简单直接,通过获取 DB 对象的 Statement,然后判断 WhereClauses 切片的长度是否大于 0。
适合不需要排除默认条件的场景。
package main
import (
"fmt"
"gorm.io/driver/mysql"
"gorm.io/gorm"
)
// User 定义用户模型
type User struct {
gorm.Model
Name string
Age int
}
// HasWhereCondition 判断DB对象是否存在WHERE条件(基础版)
func HasWhereCondition(db *gorm.DB) bool {
// 获取Statement对象
stmt := db.Statement
// 判断WhereClauses长度是否大于0
return len(stmt.WhereClauses) > 0
}
func main() {
// 连接数据库
dsn := "root:123456@tcp(127.0.0.1:3306)/test_db?charset=utf8mb4&parseTime=True&loc=Local"
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
if err != nil {
panic("failed to connect database")
}
// 1. 无WHERE条件的情况
db1 := db.Model(&User{})
fmt.Println("db1 是否有WHERE条件:", HasWhereCondition(db1)) // 输出:false
// 2. 有自定义WHERE条件的情况
db2 := db.Model(&User{}).Where("age > ?", 18)
fmt.Println("db2 是否有WHERE条件:", HasWhereCondition(db2)) // 输出:true
// 3. 软删除模型的情况(会自动添加deleted_at IS NULL)
db3 := db.Model(&User{}) // User包含gorm.Model,自带软删除字段
fmt.Println("db3 是否有WHERE条件:", HasWhereCondition(db3)) // 输出:true
}方法二:排除默认条件
如果项目中使用了软删除功能,GORM 会自动为查询添加 deleted_at IS NULL 条件,此时用方法一判断会返回 true,但这并不是我们自定义的条件。
这种情况下,我们需要排除这类默认条件,只判断是否有自定义添加的 WHERE 条件。
实现思路是遍历 WhereClauses,过滤掉软删除等默认条件,再判断剩余条件的长度。
package main
import (
"fmt"
"gorm.io/driver/mysql"
"gorm.io/gorm"
"gorm.io/gorm/clause"
)
// User 定义用户模型(含软删除)
type User struct {
gorm.Model
Name string
Age int
}
// HasCustomWhereCondition 判断是否存在自定义WHERE条件(排除默认条件)
func HasCustomWhereCondition(db *gorm.DB) bool {
stmt := db.Statement
if len(stmt.WhereClauses) == 0 {
return false
}
// 遍历WhereClauses,排除软删除等默认条件
for _, where := range stmt.WhereClauses {
// 软删除的默认条件是 "deleted_at IS NULL",对应的Expr是clause.Eq表达式
if eq, ok := where.Expr.(clause.Eq); ok {
// 判断是否是软删除的默认条件
if col, ok := eq.Column.(clause.Column); ok && col.Name == "deleted_at" && eq.Value == nil {
// 如果只有软删除这一个条件,返回false
if len(stmt.WhereClauses) == 1 {
return false
}
// 有其他条件则继续判断
continue
}
}
// 存在非默认条件,返回true
return true
}
// 所有条件都是默认条件,返回false
return false
}
func main() {
dsn := "root:123456@tcp(127.0.0.1:3306)/test_db?charset=utf8mb4&parseTime=True&loc=Local"
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
if err != nil {
panic("failed to connect database")
}
// 1. 软删除模型,无自定义条件
db1 := db.Model(&User{})
fmt.Println("db1 是否有自定义WHERE条件:", HasCustomWhereCondition(db1)) // 输出:false
// 2. 软删除模型,加自定义条件
db2 := db.Model(&User{}).Where("age > ?", 18)
fmt.Println("db2 是否有自定义WHERE条件:", HasCustomWhereCondition(db2)) // 输出:true
// 3. 非软删除模型,加自定义条件
type Product struct {
ID int
Name string
}
db3 := db.Model(&Product{}).Where("id = ?", 1)
fmt.Println("db3 是否有自定义WHERE条件:", HasCustomWhereCondition(db3)) // 输出:true
}常见问题
Q1:GORM v1 版本可以用这种方法吗?
不可以。
本文介绍的方法基于 GORM v2 版本的 Statement 结构体,而 GORM v1 版本的内部实现差异很大,没有 WhereClauses 这个字段。如果使用 v1 版本,建议先升级到 v2 版本,v2 版本在性能和功能上都有显著提升。
Q2:除了软删除,还有其他默认条件需要排除吗?
常见的默认条件主要是软删除的 deleted_at IS NULL。
另外,如果使用了 GORM 的一些插件,插件可能会自动添加默认条件,此时需要根据插件的实现逻辑,在遍历 WhereClauses 时排除对应的条件。
Q3:判断结果不准确,可能是什么原因?
首先检查 GORM 版本是否为 v2;其次确认是否有插件自动添加了 WHERE 条件;最后检查代码中是否有隐性修改 DB 对象的逻辑(比如在其他函数中对同一个 DB 对象添加了条件)。
建议在判断前打印 stmt.WhereClauses 的内容,辅助排查问题。
Q4:这种判断方法会影响性能吗?
不会。
因为我们只是读取 DB 对象内部 Statement 结构体的属性,没有执行数据库查询,也没有修改 DB 对象的核心逻辑,性能开销可以忽略不计。
Q5:如何重置 where 条件?
可以通过设置 Session 的方式要构造一个新的 db 句柄,不同场景我封装了不同的函数,可供参考复用:
// CloneDB 只会复制 db.Statement where 条件
func CloneDB(db *gorm.DB) *gorm.DB {
return db.WithContext(context.Background())
}
// NewDB 完全新创建一个 DB
func NewDB(db *gorm.DB) *gorm.DB {
return db.Session(&gorm.Session{NewDB: true, Context: context.Background()})
}
// CleanDB 使用原有 context 创建一个新的 DB
func CleanDB(db *gorm.DB) *gorm.DB {
return db.Session(&gorm.Session{NewDB: true, Context: db.Statement.Context})
}总结
判断 GO GORM 中 DB 对象是否存在 WHERE 条件,核心是利用 Statement 结构体的 WhereClauses 字段。
基础场景下直接判断该字段长度即可;如果需要排除软删除等默认条件,可通过遍历 WhereClauses 过滤特定条件。
这种方法不仅实用,而且性能优异,适合在通用查询函数、中间件等场景中使用。
大家可以根据自己的项目需求,选择对应的实现方法。如果在使用过程中遇到其他特殊场景,欢迎在评论区交流讨论。
版权声明
未经授权,禁止转载本文章。
如需转载请保留原文链接并注明出处。即视为默认获得授权。
未保留原文链接未注明出处或删除链接将视为侵权,必追究法律责任!
本文原文链接: https://fiveyoboy.com/articles/go-gorm-where-len/
备用原文链接: https://blog.fiveyoboy.com/articles/go-gorm-where-len/