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
}
相关推荐
努力学习的小廉7 分钟前
深入了解linux系统—— POSIX信号量
linux·运维·服务器
qq_1728055921 分钟前
Go 装饰器模式学习文档
学习·golang·装饰器模式
广州腾科助你拿下华为认证33 分钟前
PostgreSQL认证_PGCM考试难度有多大?
数据库·postgresql
代码的余温35 分钟前
Oracle RAC认证矩阵:规避风险的关键指南
数据库·oracle·矩阵
田野里的雨35 分钟前
manticore离线安装(Ubuntu )
linux·运维·服务器·全文检索
白鲸开源36 分钟前
一行代码引发 12G 内存 5 分钟爆仓!SeaTunnel Kafka 连接器"内存溢出"元凶抓到了
数据库·kafka·开源
疾风铸境1 小时前
项目研发实录:电子称SDK封装dll给到QT和C#调用
linux·服务器·网络
cellurw1 小时前
Day39 SQLite数据库操作与HTML核心API及页面构建
数据库·sqlite·html
小蒜学长1 小时前
旅行社旅游管理系统的设计与实现(代码+数据库+LW)
java·数据库·spring boot·后端·旅游
Mr_hwt_1231 小时前
基于mybatis-plus动态数据源实现mysql集群读写分离和从库负载均衡教程(详细案例)
数据库·spring boot·mysql·mybatis·mysql集群