Golang 1.6 数据库 NULL 值遇到 JSON 和模板 (Template)

当数据库的列允许 NULL 时,首先不太理解为什么Go不允许遇到 NULL 直接设置相应类型的默认空值,就像在Go中变量声明后就立即初始化成空值,而没有其他语言那种“生命后但却没有赋值或初始化的变量无法使用”的情况,既然这种空值是被默许的,为什么数据库中 NULL 不可以继续用这种空值代替呢?真是希望后续 Go 的版本做下改进。

OK,吐槽归吐槽,目前当然是不支持这样,所以 Scan 函数必须传入一个支持对 NULL 做特殊处理的类型,比如 sql.NullInt64 , sql.NullString 等,对于某些特殊类型,可能还需要使用其他三方 Library 的支持,比如 MySQL Driver 中的 mysql.NullTime 类型,用于支持 time.Time 的 Nullable 类型。

好了问题来了,对于后台,输出数据方式通常就是:

提供返回 JSON 的 API,前端页面AJAX请求然后构建页面。
直接构造页面,内部模板引擎构建页面,这样有利于 SEO。
无论是上述哪种方式,目前这些 Nullable 类型都会稍微有些麻烦,对于 JSON 输出,许多 Nullable 类型没有改写 MarshalJSON 方法,比如之前写的一篇文章: Golang 1.6: mysql.NullTime 没有改写 MarshalJSON 的问题

而对于在 Go 中构建页面, text/template 中的很多功能不会自动判断 Nullable 类型,比如 {{if .Property}} ,如果 Property 属性是 Nullable 类型,且他是 NULL 即 Valid 属性为 false ,但是模板中的 if 还是会认为 true ,因为这个 Nullable 类型本身是一个值所以 if 会认为是 true ,而 Nullable 类型本身到底是不是 NULL 根本没有意义,所以必须要写 {{if .Property.Valid}} 。在输出上,也要写 {{.Property.String}} (不同 Nullable 类型值得属性会不一样,这里以 sql.NullString 演示)。

或者用实际代码演示这个问题:

package main

import (
    "bytes"
    "database/sql"
    "fmt"
    "text/template"
)

type Test struct {
    EmptyString    sql.NullString
    NonEmptyString sql.NullString
}

func main() {
    test := &Test{}
    test.EmptyString = sql.NullString{Valid: false}
    test.NonEmptyString = sql.NullString{Valid: true, String: "Mgen"}
    template := template.Must(template.New("test").Parse("{{if .EmptyString}}{{.NonEmptyString}}{{end}}"))
    buffer := &bytes.Buffer{}
    err := template.Execute(buffer, test)
    if err != nil {
        panic(err)
    }
    fmt.Print(buffer.String())
}

这段代码会输出:

{Mgen true}

验证了上面讲的两个问题:

为 NULL 的 Nullable 类型会在模板 if 中直接理解成 true .
输出问题,上面输出 {Mgen true} 实际上就是把 sql.NullString 的两个内部属性全部输出出来了。

Go中 NullString 类型定义:

type NullString struct {
        String string
        Valid  bool // Valid is true if String is not NULL
}

所以正确的模板应该这样写:

template := template.Must(template.New("test").Parse("{{if .EmptyString.Valid}}{{.NonEmptyString.String}}{{end}}"))
总之一旦遇到数据库中的 NULL ,还是会稍微有些麻烦的,目前的解决方案可供选择:

  • 数据库中尽量不存 NULL 值,或者使用 ISNULL 或 COALESCE 对 NULL 值坐下处理。
  • JSON 上对 Nullable 类型进行改造,模板定义上属性要针对 Nullable 类型的属性做判断。
  • 不需要 Nullable, NULL 值转换成空值, 这是文章开头我说的愿望:joy:,目前不支持 。

声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。

Golang 1.6 数据库 NULL 值遇到 JSON 和模板 (Template)