前言

对于数据库中的JSON类型的介绍, 请查看这篇文章。https://srackhall.top/2022/11/03/json-types-for-sqlite-and-postgresql/

使用

官方示例

其中类型datatypes.JSON实际上是gorm对go语言原生json.RawMessage类型的再封装, 并提供了一系列抽象方法。以便我们对相关数据库内的json做查询。

也就是说, 对于json的转换操作, 还是要在对应的语言端进行。只不过:

  • 用JSON类型来存储’对象转JSON后字符串’
    • 好处是, 可以利用sql提供的方法来单独获取json内某一字段。以及可以享受无需在前端执行JSON.stringify()的优势。
    • 坏处是, 无法针对整体的json字符串做一些编码加密操作。
  • 用Text类型来存储’对象转JSON后字符串’
    • 好处是, 可以针对整体的json字符串做一些编码加密操作。(比如利用GORM提供的钩子函数)
    • 坏处是, 无法使用sql提供的方法来单独获取json内的某一字段。 只能依赖读取整体json字符串文本, 然后在对应语言内转换为对应对象, 然后后访问具体字段。

使用原理图来分析:

文章核心原理图

经图中三个例子的介绍, 我们应该清楚, 使用json类型后, 在gin中会真实正确的发生什么。 而不是个人意愿的以为发生了什么。

  • 即真实正确的是将一切不为datatypes.JSON的类型————datatypes.JSON的底层实际就是[]byte, 自动使用json.Marshal()函数进行转化, 将得到的转化后返回的[]byte类型数据, 存入对应的字段, 最终存入数据库。(如果有json标准格式的string, 那么抱歉, 会再次嵌套)
  • 而不是个人意愿的, 将符合json标准格式的字符串, 直接使用 []byte() 进行强制转换后, 存入对应的字段, 最终存入数据库。

小科普:

js/ts中:

  • JSON.stringify() 用来将js/ts的变量值 转化为 标准格式的json字符串
  • JSON.parse() 用来将标准格式的json字符串 转化为 js/ts 中的变量。

go中:

  • json.Marshal() 用来将go的变量值 转化为 []byte(标准格式的json字符串)
  • json.Unmarshal() 用来将[]byte(标准格式的json字符串) 转化为 go 的变量值。
  • string和[]byte, 在go语言中是可以相互转化的, string的底层就是[]byte。 可以使用 string() 和 []byte() 进行相互转化。
    • 因此, 在go中使用从其它语言创建的 json 字符串时, 按照 json.Unmarshal 但参数类型要求, 我们需要手动显式的 使用 []byte(标准格式的json字符串) 将其转化为[]byte类型。

注意(题外话)

注: 本小节与文章名称无关。 文章名称的主要关注点是数据库中的字段的JSON类型与GORM的对应关系,而本小节主要关注点是GIN与GORM。即 gin 使用中, 必须处理的 json 标签名。(json标签名是与前端结构体全量交互的基础前提。)以及, 如果保证从gen自动生成的json标签名, 符合gin的使用需要。

无论用那种, 在使用GORM GIN 等与前端的交互中, 一定要注意在 tag 中指定好 json标签

  • json标签与最终在数据库中的变量名无关, 数据库中的变量名标签对应的是gorm:"column:estimated_time;not null"(数据库变量名标签可以与json标签不一致, 但一定要与实际的数据库字段便签保持一致——“一般情况下, 我的数据库字段标签就是由结构体生成的, 因此无需过分担心此问题”)
  • json标签应与前端结构体或类型的变量字段名完全一致(虽然’标准的’驼峰和下滑线命名可以兼容, 但若是不标准的驼峰下划线, 则无法兼容如”aaaUUID、aaa_UUID”就是一组不规则的命名,但又只能如此命名), 因此为简单起见, 只需要保持完全一致即可。(若不一致, 则会出现与前端交互的值无法正常传递的情况, 比如会产生空值。)

对于数据库列名, 无需过分关注, 无论是项数据库创建, 还是从数据库生成, 全部使用GORM默认的即可, 因此我们在models中无需显式指定字段对应的数据库列名。

对应JSON标签, 随需要显式地指定。但是由于我们的models不负责与前端的交互, 仅负责生成数据库, 因此无需在models中显式指定, 仅需要在gen配置中, 指定生成的最终json标签名即可。可参考下图:

`json标签的名字, 应该与前端的数据结构对象字段名称保持一致, 否则可能造成由GIN根据结构体返回的对象与前端需要的对象, 内部字段名称不一致的问题`

需要明确的是: json标签的使用, 主要作用与GIN框架与前端的交互中, 而GORM与数据库的交互则使用的是GORM的列名标签指定。 这里与之产生关联是因为使用了GEN框架来子对对GORM使用的结构体进行生成, 因此我们需要保证有数据库对应的列字段名生成的结构体, json标签名与前端保持一致

  • 对于go端的结构体内部字段名: 一般采用首字母大写的形式, 但因json标签名代表了与前端最终的交互结果, 因此甚至可以完全定义未随意的名称(前提是后方的json标签名需要和前端保持对应)。