在实际业务中,我们有可能把非空值更新为空值,当使用结构体更新的时候,GORM 只会更新那些非空的字段
例如下面的更新,没有东西会被更新,因为像 "", 0, false 是这些字段类型的空值
db.Model(&user).Updates(User{Address: "", Age: 0})
解决办法,修改源码:
1、通过引入一个标记:FORCE ,如果有该标记,则该字段IsBlank设置为false,
找到 scope.go文件,路径:
修改Fields方法
func (scope *Scope) Fields() []*Field {
if scope.fields == nil {
var (
fields []*Field
indirectScopeValue = scope.IndirectValue()
isStruct = indirectScopeValue.Kind() == reflect.Struct
)
for _, structField := range scope.GetModelStruct().StructFields {
if isStruct {
fieldValue := indirectScopeValue
for _, name := range structField.Names {
if fieldValue.Kind() == reflect.Ptr && fieldValue.IsNil() {
fieldValue.Set(reflect.New(fieldValue.Type().Elem()))
}
fieldValue = reflect.Indirect(fieldValue).FieldByName(name)
}
// 注释掉原来的
// fields = append(fields, &Field{StructField: structField, Field: fieldValue, IsBlank: isBlank(fieldValue)})
//有force代表强制更新
_, ok := structField.TagSettingsGet("FORCE")
fields = append(fields, &Field{StructField: structField, Field: fieldValue, IsBlank: isBlank(fieldValue) && !ok})
//如果不是struct,则返回空,因为原生没有字段这个说法
} else {
fields = append(fields, &Field{StructField: structField, IsBlank: true})
}
}
scope.fields = &fields
}
return *scope.fields
}
2、在结构体中加入标记
type User struct {
gorm.Model
Name string
Age sql.NullInt64
Address string `gorm:"index:addr;force:force;"` // 给Address 添加一个标记force
}
3、此时我们可以发现,Address为空值的时候可以更新。
但是当我们使用结构体查询的时候,默认查询这些字段的非零值,而我们添加了force标记是没有将空值的过滤掉,例如:
db.Where(&User{Name: "jinzhu", Age: 20}).First(&user)
// 实际的sql为:
// SELECT * FROM users WHERE name = "jinzhu" AND age = 20 AND Address="";
这显然不是我们想要的结果。。
4、所以我们需要再引入一个标记:REQUIRED。然后修改main.go中Update方法。遍历结构体字段,当查询到有REQUIRED标记的时候,动态将FORCE标记设置进去,从而避免了使用结构体查询的时候没有过滤空值
main.go路径:
修改Update方法:
func (s *DB) Update(attrs ...interface{}) *DB {
scope := s.NewScope(s.Value)
var indirectScopeValue = scope.IndirectValue()
var isStruct = indirectScopeValue.Kind() == reflect.Struct
for _, structField := range scope.GetModelStruct().StructFields {
if isStruct {
//有REQUIRED代表强制更新
val, ok := structField.TagSettingsGet("REQUIRED")
if ok && val == "false" {
structField.TagSettingsSet("FORCE", "force")
}
defer structField.TagSettingsDelete("FORCE")
}
}
return s.Updates(toSearchableMap(attrs...), true)
}
5、结构体添加required标记:
type User struct {
gorm.Model
Name string
Age sql.NullInt64
Address string `gorm:"index:addr;required:false;"` // 给Address 添加一个标记required
}
查询的时候过滤了空值:
db.Where(&User{Name: "jinzhu", Age: 20}).First(&user)
// 实际的sql为:
// SELECT * FROM users WHERE name = "jinzhu" AND age = 20;
更新的时候,
db.Model(&user).Updates(User{Address: ""})
// 实际sql为
// UPDATE user SET Address='' WHERE id=111