Go业务开发常用关注点

本文对实际开发场景中面对高频的场景,总结出来的一些处理方案,希望能对业务开发的同学提供帮助!

结构体转换

实际开发中会面对一个相似的数据结构,由于引用不同的包,需要开发转换到对应的结构上,本质上这些数据结构是一致的,但是所在包不同所以不能直接赋值。

常规的方案大致分为下面几种:

  1. 直接转换 struct

这种适合结构完全一致的情况,参数名和类型都必须保持一致;适用场景相对较少,面对不同包的协议转换如果包含一个枚举就无效了

go 复制代码
type aType int64
type A struct {
   a aType
}

type bType int64
type B struct {
   a bType
}

func Test(t *testing.T) {
   a := A{a: 1}
   b := B(a)
   fmt.Println(a, b)// 如果把aType和bType直接当做in64就可以正常转换
}

2. 手撸代码

开发手动转换结构,适合字段比较少的结构,同时命名不会很相似,如果相似度较高存在写错的可能,面对复杂有嵌套数据结构效率低下。

3. 正反序列化转换

这种方案相对于第一种具备更强的兼容性,可以通过 tag 来实现不同类型的转换,但是面对不同协议生成的代码还是具有局限性,同时效率比较低下,序列化是比较消耗 cpu 的操作;

需要注意的是,官方的原生 json 库处理大数存在精度丢失的问题,我们这里采用 jsonx 默认支持大数

jsonx: code.byted.org/gopkg/jsonx

go 复制代码
type aType int64
type A struct {
   A aType `json:"a"`
}

type bType int64
type B struct {
   A bType `json:"a"`
}

func Test(t *testing.T) {
   aStr := jsonx.ToString(A{1})
   b := &B{}
   _ = jsonx.UnmarshalFromString(aStr, &b)
   fmt.Println(aStr, b)
}

最佳实现

这里的最佳实现其实要区分场景来考虑:

  • 面对高并发或是简单结构的场景,需要减少资源消耗,可以采用【手撸代码】的方式实现
  • 面对并发比较低的场景,通过【正反序列化】是比较好的方案,使用起来更简单

数据库中存储json结构体

表中有extra字段,存储的是扩展信息,比如执行时间,通常的结构声明是这样的:

go 复制代码
type BaseInfo struct {
   ID            int64        `json:"id" gorm:"column:id"`
   Extra         string       `json:"extra" gorm:"column:extra"`
}

意味着查询出来结构后还需要进行 unmarshal 操作,且写入数据的时候也要进行 marshal,开发者在修改数据的时候需要额外考虑其他接口所使用的数据结构,用起来不方便。

最佳实践

gorm 是支持很多拓展特性的,通过实现Scan、Value的方法就可以省去在业务代码中序列化的操作,降低开发者的心智负担,优化后大致如下:

go 复制代码
type BaseInfo struct {
   ID            int64        `json:"id" gorm:"column:id"`
   Extra *ExtraInfo `json:"check_in_detail" gorm:"column:check_in_detail"`
}

type ExtraInfo struct {
    Info1 `json:"info1"`
}

func (BaseInfo) TableName() string {
   return "base_info"
}

// Value return json value, implement driver.Valuer interface
// 如果接受者是指针,那么就只能是指针来调用
// 如果接受者是值类型,则支持指针、值类型来调用
func (j ExtraInfo) Value() (driver.Value, error) {
   return json.Marshal(j)
}

// Scan scan value into Jsonb, implements sql.Scanner interface
// 接受者要使用指针类型,这才才能实际赋值
func (j *ExtraInfo) Scan(value interface{}) error {
   bytes, ok := value.([]byte)
   if !ok {
      return errors.New(fmt.Sprint("Failed to unmarshal JSONB value:", value))
   }
   result := ExtraInfo{}
   var err error
   if len(bytes) > 0 {
      err = json.Unmarshal(bytes, &result)
   }
   *j = result
   return err
}
相关推荐
矜持的左手3 小时前
电子小白的枕边书:电子学(The Art of Electronics)
数据库·restful
吴声子夜歌3 小时前
Redis 5.x——布隆过滤器
数据库·redis·缓存
深圳恒讯4 小时前
菲律宾云服务器与传统VPS的架构差异
运维·服务器·架构
蓝天下的守望者4 小时前
svt_apb_if里的宏定义问题
运维·服务器·数据库
小林ixn4 小时前
从“玩具工具”到“跨语言利器”:MCP 协议实战解析
运维·服务器·网络
上海云盾-小余4 小时前
网站频繁遭遇 SQL 注入溯源与原生漏洞修复全流程总结
数据库·sql
数据库小学妹4 小时前
国家区域医疗中心国产化改造实战:数据库选型、跨院区数据互通与踩坑经验
数据库·国产数据库·数据库选型·医疗信息化·信创数据库·医疗信创
AllData公司负责人6 小时前
数据库同步平台|AIIData数据中台实现OceanBase、达梦数据库、OpenGauss、人大金仓、Hive、TDengine 一键接入Doris
大数据·数据库·hive·mysql·oceanbase·tdengine
2603_954708316 小时前
全维度容错设计,打造微电网安全运行屏障
服务器·网络·数据库·人工智能·分布式·安全