Go-gorm returns 如果使用字段中具有默认值的 Struct 实例过滤的所有记录

Go-gorm returns all records if filtered with Struct instance that has default Value in a field

假设我们有以下结构:

type Task struct {
    ...
    Completed bool      `gorm:"default:false"                  json:"-"`
}

MySQL 数据库中有 5 个条目:

我面临以下特点:

db, err = gorm.Open("mysql", connstr)
var ret []Task

// This returns 3 rows
db.Debug().Model(&Task{}).Find(&ret, "Completed =?", false)
// This returns 2 rows
db.Debug().Model(&Task{}).Find(&ret, Task{Completed: true})
// BUT THIS RETURNS 5 ROWS!
db.Debug().Model(&Task{}).Find(&ret, Task{Completed: false})

知道为什么会这样吗?在最后一次调用时执行的 SQL 是: SELECT * FROM 'tasks'

我想避免为每个结构字段编写新的 SQL 查询。传递结构(对象)似乎更明智。

原因

GORM 查看提供给 Find() 的结构字段,并检查设置了哪些字段,以构建查询。因此在第二个示例中,它看到 Completed 设置为 true,并向查询添加 WHERE 子句:

db.Debug().Model(&Task{}).Find(&ret, Task{Completed: true})

生成:

SELECT * FROM "tasks" WHERE "tasks"."completed" = true

在第二个示例中,GORM 无法知道您是传入了 Task{Completed: false} 还是简单地传入了 Task{},因为 false 是 [=21] 的 zero-value =].

如果您有一个 int 字段并尝试查询 0:

,同样的事情也会发生
db.Debug().Model(&Task{}).Find(&ret, Task{Num: 0}) // Generates: SELECT * FROM 'tasks'

解决方案

如果您真的想使用该结构进行查询,您可以更改模型,使 Completed 成为 *bool 而不是 bool。由于指针的 零值 为 nil,提供指向 false 的指针将告诉 GORM 添加该子句:

trueBool := true
falseBool := false

db.Debug().Model(&Task{}).Find(&ret, Task{Completed: &falseBool})
db.Debug().Model(&Task{}).Find(&ret, Task{Completed: &trueBool})

生成(分别)

SELECT * FROM "tasks" WHERE "tasks"."completed" = false
SELECT * FROM "tasks" WHERE "tasks"."completed" = true

请记住,这样做意味着如果未设置 Completed 可以在数据库中保存为 NULL