当前位置: 首页 > news >正文

【GoLang】【框架学习】【GORM】4. 使用 BeforeUpdate hook 操作时,出现反射报错

文章目录

  • 0. 问题背景
  • 1. 问题环境
  • 2. 问题排查
  • 3. 总结

0. 问题背景

对于大多的业务操作的增删查改来说,都会记录一个 创建者、更新者。我们的业务上是通过 GORM hook 的方式来进行处理的。在 create 的时候用的挺顺的,但在 update 的时候出现反射的报错,最终分析代码后,可能是我们使用方式有误,或者 GORM 本身不支持该操作导致的。

在 Github 上找到了同样遇见此问题的 issue,但没有解决,我在下面也做了相应的评论:

  • https://github.com/go-gorm/gorm/issues/7170

1. 问题环境

对业务做了简单抽象,后 ORM 结构定义:

type User struct {BaseModelOperName  stringAge   intEmail string
}func (u *User) TableName() string {return "user"
}type BaseModelOper struct {ID        int64          `gorm:"primary_key;auto_increment:true;type:bigint" json:"id"`CreatedAt time.Time      `json:"created_at" gorm:"type:timestamp;autoCreateTime:milli"`UpdatedAt time.Time      `json:"updated_at" gorm:"type:timestamp;autoUpdateTime:milli"`DeletedAt gorm.DeletedAt `json:"deleted_at" gorm:"type:timestamp"`CreatedBy int64          `json:"created_by" gorm:"type:bigint"`UpdatedBy int64          `json:"updated_by" gorm:"type:bigint"`
}func (*BaseModelOper) BeforeCreate(db *gorm.DB) error {userId := GetUserIdFromCtx(db.Statement.Context)if userId != 0 {db.Statement.SetColumn("created_by", userId)}return nil
}func (*BaseModelOper) BeforeUpdate(db *gorm.DB) error {userId := GetUserIdFromCtx(db.Statement.Context)if userId != 0 {db.Statement.SetColumn("updated_by", userId)}return nil
}

update 操作:

// 查询条件
type UserCond struct {Id *int64 `json:"id" gorm:"type:bigint"`
}
// 更新结构
type UserUpdate struct {Name *string
}func (u *User) UpdateByCond(ctx context.Context, cond UserCond, updateV *UserUpdate) error {query, values := util.MakeStructQuery(cond)err = dbcon.CtxDB(ctx).Model(&User{}).Where(query, values...).Updates(updateV).Errorif err != nil {logger.Errorf(ctx, "UpdateByCond has err. %+v", err)return err}return nil
}

建表、数据插入、数据更新操作:

func TestUpdateBy() {if err := dbcon.GetDB().AutoMigrate(&User{}); err != nil {panic(err)}ctx := context.Background()ctx = SetUserIdToCtx(1)u := User{Age:   21,Name:  "AAA",Email: "xrc@xrc.com111",}if err := dbcon.CtxDB(ctx).Create(&u).Error; err != nil {panic(err)}if err := u.UpdateByCond(ctx,UserCond{Id: proto.Int64(1)},&UserUpdate{Name: proto.String("BBB")}); err != nil {panic(err)}
}

在这里插入图片描述
出现了 panic 错误!

2. 问题排查

在这里插入图片描述

  • 实际上就是设置单列的更新的操作。
  • 有几个关键信息点需要先了解:
    • stmt.Dest 就是我们传递进来的精简结构体 UserUpdate。其中不包含 UpdatedBy 这些基础字段,仅包含业务字段。
    • stmt.Schema 是 ORM 结构体,也就是对数据表映射的结构体。包含全部字段,即 基础字段+业务字段。

流程如下:
在这里插入图片描述

  • 注意上面的 field.Set 传入的是 destValue,这里的 destValue 字段是 ORM 结构的精简版,并不包含全体字段。
    在这里插入图片描述

  • 这里的 field 是 ORM 结构体的结构,是 0,5

    • 0 指的是 ORM 结构体中第一个匿名结构体
    • 5 是这个匿名结构体中的第 5 个字段
    • 也就是说,需要通过反射,找到结构体里面的 [0, 5] 字段,给他赋值。
      在这里插入图片描述
      在这里插入图片描述
  • 但是这里的 v 是 dest 反射值的解引用,实际上是一个不完整的结构,能看到里面只有一个字段。
    在这里插入图片描述

  • 所以当 i=5 的时候,在 dest 中就找不到对应的字段,那么自然就会报错。

3. 总结

正如我在上面 issue 的评论一样:

这块没明白为何要这样要求?在 SetColumn 的操作里面,dest 如果是结构体类型的话,就需要使用表对应的结构体类型才行。针对 create 类的操作确实没啥问题。而 update 操作既然已经支持了 struct/map 两种更新方式,为何在这又不支持自定义 struct 更新了呢?
涉及到一些代码逻辑的历史问题,简单的将自定义结构体通过序列化转换成了 map 结构才得以暂时解决…

既然 update 操作支持通过 struct 的方式去做更新,为何不支持的更彻底一点???先如今用 map 的方式去转换处理,代码属实冗余。

http://www.dtcms.com/a/553819.html

相关文章:

  • 有哪些好点的单页网站公司管理制度完整版
  • 庭田科技亮相成都复材盛会,以仿真技术赋能产业革新
  • 网站安全认证去哪做国内十大咨询公司排名
  • Maven高级-分模块设计与开发
  • markdown转为pdf导出
  • python - day 11
  • 污水处理厂三菱FX5U系列PLC通过Modbus TCP转CCLKIE工业智能网关和多个不同的仪表进行通讯案例
  • 东莞专业网站设计建站公司无锡网站推广优化费用
  • 海南网络公司网站建设wordpress 禁止收录
  • 哪里有学习做网站的html网站模板 免费
  • 网站中的表格phonegap下载
  • JAVA攻防-专题漏洞SPEL表达式SSTI模版Swagger接口Actuator泄露Spring特检
  • vue-day03
  • 高效稳定的命理测算平台:基于Linux+Nginx+PHP+MySQL的技术架构解析
  • 威海做网站哪家好西数网站助手
  • 企业商旅平台推荐:合思——全流程合规管控与生态协同标杆
  • 专业电商网站开发自己做图网站
  • 【flutter报错:Build failed due to use of deprecated Android v1 embedding.】
  • git 命令里的存档和检出的区别
  • 杰理芯片SDK-杰理SDK工程框架介绍
  • 家教中介网站怎么做学员引流用万网做网站
  • C# TaskCompletionSource.SetResult 用法详解
  • 网站建设模板html网站开发工程师岗位职责要求
  • 【Linux network和NetworkManager双网卡主备模式绑定】
  • SSO 单点登录
  • Linux 中如何查看系统的位数
  • 云南建设企业网站修改wordpress首页缩略图尺寸
  • 网站的投票系统怎么做wordpress文章类模板
  • AI智能体如何让用户洞察更简单、更快速、更精准
  • 离线测试与在线测试