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
}
相关推荐
Hellc0079 分钟前
Docker部署BaGet私有NuGet服务器:从入门到备份恢复完整指南 二
服务器·docker·eureka
e***193536 分钟前
【SqlServer】SQL Server Management Studio (SSMS) 下载、安装、配置使用及卸载——保姆级教程
数据库·sqlserver
6***B4837 分钟前
存储过程(SQL)
android·数据库·sql
小马爱打代码1 小时前
避坑指南:MySQL 迁移到 TiDB
数据库·mysql·tidb
会飞的土拨鼠呀2 小时前
通过Linux进程id找到程序路径
linux·服务器·网络
一个天蝎座 白勺 程序猿2 小时前
Apache IoTDB(10):数据库操作——从查询到优化的全链路实践指南
数据库·apache·时序数据库·iotdb
普普通通的南瓜2 小时前
IP证书在关键信息基础设施安全防护中的实践与挑战
网络·数据库·网络协议·tcp/ip·安全·ssl
合作小小程序员小小店2 小时前
桌面开发,超市管理系统开发,基于C#,winform,sql server数据库
开发语言·数据库·sql·microsoft·sqlserver·c#
quweiie2 小时前
thinkphp8.0链接SQL SERVER2022数据库
数据库·sqlserver·thinkphp
Databend3 小时前
如何打造AI时代的数据基石 | Databend Meetup 上海站
数据库