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
}
相关推荐
山茶花.1 小时前
SQL注入总结
数据库·sql·oracle
m0_736919101 小时前
超越Python:下一步该学什么编程语言?
jvm·数据库·python
landonVM1 小时前
Linux 上搭建 Web 服务器
linux·服务器·前端
m0_748229992 小时前
ThinkPHP快速入门:从零到实战
c语言·开发语言·数据库·学习
云游云记2 小时前
nesbot/carbon 常用功能总结
linux·运维·服务器
阿蒙Amon2 小时前
C#每日面试题-Thread.Sleep和Task.Delay的区别
java·数据库·c#
landonVM2 小时前
Linux 下的高效压缩工具 Zstandard
linux·运维·服务器
遇见火星2 小时前
服务器运维操作命令速查手册
运维·服务器
沉舟侧畔千帆过_2 小时前
一个DBA的真心话:搞定Oracle+PG双库,我就靠这招
数据库·oracle·dba
醉风塘2 小时前
【终极解决方案】Oracle ORA-01795错误:IN列表1000条限制的全面突破指南
数据库·oracle