分享
  1. 首页
  2. 文章

Database Go and JSON

qgymje · · 2821 次点击 · · 开始浏览
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

在使用Go开发web项目的过程中, 数据库读写操作与JSON格式的输入输出是两块最基础的模块, Go的标准库已经帮我们做了很多, 熟悉database/sql与encoding/json这两个库能帮我们更自在地开发web应用.

但此篇文章抛开基础不说, 只说一些在开发中遇到一些真实存在的痛点.

如何处理Null值?

Go的一大特色就是zero value, 比如int类型的zero value是0, string为"", struct为每个field里各自类型的zero value. 因此在Go的很多ORM处理NULL值时, 都是通过zero value机制入库或出库的, 因此, 使用ORM操作的数据库, 如何没有明确指明, 基本上看不到NULL值. 一个可能为NULL的varchar字段, 存入了""(空字符串).

这里不讨论关于空字符串与NULL的优劣, 而是说明如何处理NULL值.

sql标准库里带了这么几个类型: ```sql.NullString, sql.NullInt64, sql.NullBool, sql.NullFloat64```, 如果在定义struct里field类型的时候, 使用sql.NullString代替string的话, 就可以在数据库里存入NULL值.

通过源码看到, NullString的结构与两个方法:

type NullString struct{
 String string
 Valid bool
}
Scan(value interface{}) error
Value()(driver.Value, error)

Scan实现了sql.Scanner接口, Value实现了driver.Valuer接口. 这两个接口表示数据从Go端与DB端的转换. Scan用于数据从DB转到Go时被调用,也就是说select时用(查看源码); 而Value则由Go转到DB时被调用. 也就是说insert/update时候用(查看源码).

在两个不同领域做数据转换时, 通常用接口来定义, 这是接口的作用, JSON也是同样如此.

有了接口的帮助, 任何实现了这两个接口的类型, 都能在DB与Go之间做转换, 接口建立起了桥梁.

如何处理自定义类型?

由上一节可以想到, 如果在想要保存Go自定义类型到DB里, 只需要实现Scanner与Valuer接口, 就像sql.NullString一样.

典型如NullTime(查看源码):

type NullTime struct {
 Time time.Time
 Valid bool
}
func (nt *NullTime) Scan(value interface{}) error {
 nt.Time, nt.Valid = value.(time.Time)
 return nil
}
func (nt NullTime) Value() (driver.Value, error) {
 if !nt.Valid {
 return nil, nil
 }
 return nt.Time, nil
}

为了让这个类型更好用, 添加一个帮助函数:

func ToNullTime(t time.Time) NullTime {
 return NullTime{Time: t, Valid: !t.IsZero()}
}

自定义类型如何转化成JSON?

如果拿一个sql.NullString的类型, 做json.Marshal, 那么得到输出是这样的:

{
 ...
 "column_name" : { "String" : "Hello?", "Valid" : true },
 ...
}
//而我们想要的是:
{
 ...
 "column_name" : "Hello?",
 ...
}
//or
{
 ...
 "column_name" : null,
 ...
}

我们知道, json库有两个标准的接口, json.Marshaler(查看源码) , json.Unmarshaler(查看源码), 如果一个类型实现此两个接口, 则在使用json.Marshal/Unmarshal调用时, 调用此类型的自定义方法:

type NullString struct {
 sql.NullString
}
func(v *NullString) MarshalJSON() ([]byte, error) {
 if v.Valid {
 return json.Marshal(v.String)
 } else {
 return json.Marshal(nil)
 }
}
func (v NullString) UnmarshalJSON(data []byte) error {
 var s *string
 if err := json.Unmarshal(data, &s); err != nil {
 return err
 }
 if s != nil {
 v.Valid = true
 v.String = *s
 } else {
 v.Valid = false
 }
 return nil
}

总结

Go语言的接口, 扮演了桥梁的角色, 连接起了Go与其它领域的数据转换, 通过实现标准库的接口, 我们可以让Go的数据类型轻松适应不同数据领域.


[参考]
http://dennissuratna.com/marshalling-nullable-string-db-value-to-json-in-go/

http://blog.carbonfive.com/2015/07/09/there-will-be-sql/

http://marcesher.com/2014/10/13/go-working-effectively-with-database-nulls/


有疑问加站长微信联系(非本文作者)

本文来自:博客园

感谢作者:qgymje

查看原文:Database Go and JSON

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

关注微信
2821 次点击
2 回复 | 直到 2000年01月01日 00:00:00
暂无回复
添加一条新回复 (您需要 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传

用户登录

没有账号?注册
(追記) (追記ここまで)

今日阅读排行

    加载中
(追記) (追記ここまで)

一周阅读排行

    加载中

关注我

  • 扫码关注领全套学习资料 关注微信公众号
  • 加入 QQ 群:
    • 192706294(已满)
    • 731990104(已满)
    • 798786647(已满)
    • 729884609(已满)
    • 977810755(已满)
    • 815126783(已满)
    • 812540095(已满)
    • 1006366459(已满)
    • 692541889

  • 关注微信公众号
  • 加入微信群:liuxiaoyan-s,备注入群
  • 也欢迎加入知识星球 Go粉丝们(免费)

给该专栏投稿 写篇新文章

每篇文章有总共有 5 次投稿机会

收入到我管理的专栏 新建专栏