Golang中gorm无法将字段更新为空值

 更新时间:2023年05月25日 16:01:07   作者:空心树无芽  
本文主要介绍了Golang中gorm无法将字段更新为空值,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

在使用gorm将一个字段更新为空的时候,发现并不生效,不了解具体什么原因,所以此时需要打开debug模式,查看原生SQL是如何执行的。

type Student struct {
    Model
    Email       string  `form:"email" json:"email"`
    Name         string    `form:"name"  json:"name"`
}
func(c *Content) update(content Content) (err) {
    err = db.Model(&Content{}).Debug().Where("id = ?", 123).Update(&content).Error
}

查看日志便知,此时如果name为空字符串,那么update的sql语句中并不会set,后查阅,方知gorm对于空字符和0这种数据,认为是不需要处理的,所以。。。

遇到这个问题,有两种解决方案:

A.更新传值的时候通过map来指定;

B.修改gorm的源码包,让它支持自定义是否可以设置为空值;

上述两种方案,第一种比较简单,不过感觉比较low,所以我选择尝试第二种。

当然第二种,也有它的问题,比如被更新之后,得手动去调整回来。

下面重点讲解第二种方案的实施。

1、找到gorm包下的scope.go文件

func convertInterfaceToMap(values interface{}, withIgnoredField bool, db *DB) map[string]interface{} {
    var attrs = map[string]interface{}{}
    switch value := values.(type) {
    case map[string]interface{}:
        return value
    case []interface{}:
        for _, v := range value {
            for key, value := range convertInterfaceToMap(v, withIgnoredField, db) {
                attrs[key] = value
            }
        }
    case interface{}:
        reflectValue := reflect.ValueOf(values)
        switch reflectValue.Kind() {
        case reflect.Map:
            for _, key := range reflectValue.MapKeys() {
                attrs[ToColumnName(key.Interface().(string))] = reflectValue.MapIndex(key).Interface()
            }
        default:
            for _, field := range (&Scope{Value: values, db: db}).Fields() {
                if !field.IsBlank && (withIgnoredField || !field.IsIgnored) {
                    attrs[field.DBName] = field.Field.Interface()
                }
            }
        }
    }
    return attrs
}

上面代码表示我们传递过来的数据会被转为map型,然后再进行数据库字段更新,这个代码很简单,就是把满足条件保存到map。

我们要解决的是空值能够更新,则和field.IsBlank相关联,接着找到下一个方法;

// Fields get value's 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)
                }
                //判断model中字段有没有force字段,此处使用Force,是因为解析tag时,统一转成了大写
                _, ok := structField.TagSettingsGet("FORCE")
                //如果字段为空值,且字段存在则设定为false
                fields = append(fields, &Field{StructField: structField, Field: fieldValue, IsBlank: isBlank(fieldValue) && !ok})
            } else {
                fields = append(fields, &Field{StructField: structField, IsBlank: true})
            }
        }
        scope.fields = &fields
    }
    return *scope.fields
}

注意上面带注释的两行,作用就不再赘述了。

2、修改我们的model

type Student struct {
    Model
    Email       string  `gorm:"force" form:"email" json:"email"`
    Name         string    `gorm:"force" form:"name"  json:"name"`
}
func(c *Content) update(content Content) (err) {
    err = db.Model(&Content{}).Debug().Where("id = ?", 123).Update(&content).Error
}

此时执行则顺利完成,SQL语句也包含了所有字段。

3、你有没有感到奇怪,为什么scope里面校验的是FORCE,而我在model中则定义的是force?

这就要说到另一个文件 gorm包下的 model_struct.go

func parseTagSetting(tags reflect.StructTag) map[string]string {
    setting := map[string]string{}
    for _, str := range []string{tags.Get("sql"), tags.Get("gorm")} {
        if str == "" {
            continue
        }
        tags := strings.Split(str, ";")
        for _, value := range tags {
            v := strings.Split(value, ":")
            k := strings.TrimSpace(strings.ToUpper(v[0]))
            if len(v) >= 2 {
                setting[k] = strings.Join(v[1:], ":")
            } else {
                setting[k] = k
            }
        }
    }
    return setting
}

这个方法表示将orm中定义的tag标签,全部解析并转化为大写。

到此这篇关于Golang中gorm无法将字段更新为空值的文章就介绍到这了,更多相关gorm 字段更新为空值内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Golang之sync.Pool对象池对象重用机制总结

    Golang之sync.Pool对象池对象重用机制总结

    这篇文章主要对Golang的sync.Pool对象池对象重用机制做了一个总结,文中有相关的代码示例和图解,具有一定的参考价值,需要的朋友可以参考下
    2023-07-07
  • Go1.21新增cmp包的用法详解

    Go1.21新增cmp包的用法详解

    Go 1.21新增的 cmp 包提供了与比较有序值相关的类型和函数,前几篇文章讲解的 slices 包中的函数有大量使用到 cmp 包中的函数和类型,下面我们就来看看cmp包的相关函数用法吧
    2023-08-08
  • Go环境变量配置,及GOROOT、GOPATH的区别小结

    Go环境变量配置,及GOROOT、GOPATH的区别小结

    本文主要介绍了Go环境变量配置,及GOROOT、GOPATH的区别小结,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2024-09-09
  • Golang 实现interface类型转string类型

    Golang 实现interface类型转string类型

    这篇文章主要介绍了Golang 实现interface类型转string类型的操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2021-04-04
  • Golang中tinyrpc框架的源码解读详解

    Golang中tinyrpc框架的源码解读详解

    tinyrpc是一个高性能的基于protocol buffer的rpc框架。项目代码非常少,很适合初学者进行golang的学习。本文将从源码的角度带大家了解tinyrpc框架的使用,需要的可以参考一下
    2023-01-01
  • Go语言中init函数特点、用途和注意事项详解

    Go语言中init函数特点、用途和注意事项详解

    go语言中有一个非常神奇的函数init,它可以在所有程序执行开始前被执行,并且每个package下面可以存在多个init函数,这篇文章主要给大家介绍了关于Go语言中init函数特点、用途和注意事项的相关资料,需要的朋友可以参考下
    2023-07-07
  • Go语言原子操作atomic的使用

    Go语言原子操作atomic的使用

    本文介绍了Go语言原子操作的使用方法,原子操作是一种无锁的技术,可通过CPU指令实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2024-10-10
  • Go使用proto3的踩坑实战记录

    Go使用proto3的踩坑实战记录

    这篇文章主要给大家介绍了关于Go使用proto3的踩坑记录,文中通过实例代码介绍的非常详细,对大家学习或者会用Go语言具有一定的参考学习价值,需要的朋友可以参考下
    2023-02-02
  • Go语言实现的排列组合问题实例(n个数中取m个)

    Go语言实现的排列组合问题实例(n个数中取m个)

    这篇文章主要介绍了Go语言实现的排列组合问题,结合实例形式分析了Go语言实现排列组合数学运算的原理与具体操作技巧,需要的朋友可以参考下
    2017-02-02
  • 详解Go语言中for循环,break和continue的使用

    详解Go语言中for循环,break和continue的使用

    这篇文章主要通过一些示例为大家介绍一下Go语言中for循环、break和continue的基本语法以及使用,文中的示例代码讲解详细,感兴趣的小伙伴可以了解一下
    2022-06-06

最新评论