在使用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/

Database Go and JSON的更多相关文章

  1. JSON Patch

    1.前言 可以这么说的是,任何一种非强制性约束同时也没有"标杆"工具支持的开发风格或协议(仅靠文档是远远不够的),最终的实现上都会被程序员冠上"务实"的名头,而 ...

  2. GO语言的开源库

    Indexes and search engines These sites provide indexes and search engines for Go packages: godoc.org ...

  3. jquery easy ui 1.3.4 数据表格(DataGrid)(8)

    8.1.创建DataGrid html代码 <table id="dg"></table> $("#dg").datagrid({ // ...

  4. 【转】简单的 Laravel 5 REST API

    Introduction Almost all successful internet based companies have APIs. API is an acronym for Applica ...

  5. Chp10: Scalability and Memory Limits

    The Step-by-Step Approach break down a tricky problem and to solve problems using what you do know. ...

  6. 利用 Composer 一步一步构建自己的 PHP 框架(四)——使用 ORM

    本教程示例代码见 https://github.com/johnlui/My-First-Framework-based-on-Composer 回顾 经过前三篇文章 基础准备 . 构建路由 和 设计 ...

  7. MongoDB:The Definitive Guide CHAPTER 2 Getting Started

    MongoDB is very powerful, but it is still easy to get started with. In this chapter we’ll introduce ...

  8. Go语言(golang)开源项目大全

    转http://www.open-open.com/lib/view/open1396063913278.html内容目录Astronomy构建工具缓存云计算命令行选项解析器命令行工具压缩配置文件解析 ...

  9. Django: 之数据库导入、迁移和联用

    Django 数据库导入 从网上下载的一些数据,excel表格,xml文件,txt文件等有时候我们想把它导入数据库,应该如何操作呢? 以下操作符合 Django版本为 1.6 ,兼顾 Django 1 ...

随机推荐

  1. 环形队列C++实现

    大家好,我是小鸭酱,博客地址为:http://www.cnblogs.com/xiaoyajiang 以下鄙人用C++实现了环形队列 /******************************** ...

  2. MyFirstStruts2

    package com.sdlc.action; public class HelloWorldAction { private String msg; public void setMessage( ...

  3. [置顶] Android四大组件之BroadcastReceiver

    Android四大组件之BroadcastReceiver Broadcast Receiver 广播接收器,是一种负责接收广播消息并对消息做出响应的组件,和Service一样并不提供与用户交互的UI ...

  4. php-timeit估计php函数的执行时间

    首先,前段时间利用手头的日本VPS搭建了一个google代理,访问速度还行,分享给大家: 谷歌 谷歌:guge119.com 谷歌学术:scholar.guge119.com 有时候我们在PHP性能优 ...

  5. linux 网络Socket实战

    Preface:就算调通API,也不值得太过自豪!!!悉心细心学习,最好的深度学习就是看-学...*.h/*.class ProtoType; 1,linux C ftp C/S简单实现 ftpS端码 ...

  6. MBI 跨国网络传销 金字塔诈骗 解密

    马来西亚  反传销博客地址http://combatingillegalpyramidscheme.blogspot.jp/search/label/Mface 需要FQ访问  闽渝警方协作抓获一名公 ...

  7. hdu 5256 序列变换(LIS最长上升子序列)

    Problem Description 我们有一个数列A1,A2...An,你现在要求修改数量最少的元素,使得这个数列严格递增.其中无论是修改前还是修改后,每个元素都必须是整数. 请输出最少需要修改多 ...

  8. 平时的笔记02:处理fnmatch模块

    # Copyright 2006 Joe Wreschnig## This program is free software; you can redistribute it and/or modif ...

  9. Oracle通过指令创建用户

    Oracle作为世界上使用最广泛的关系数据库,对于客户很多每天海量数据的公司是首要选择.我们公司在双十一期间,曾发生过每网点每天1G多的扫描数据量,全国有六千多个网点,每天每时不停读写数据库,而数据库 ...

  10. hdoj 3400 三分

    两次三分 #include <iostream> #include <cstdio> #include <cstring> #include <cmath&g ...