Goでnull.Stringを扱う
背景
最近、GoでPATCH APIを実装する機会があり、その時、null.String を使ってリクエストを制御する必要があったので、この辺りを改めてキャッチアップしてみました。
Goでは、nullという概念はありません。
stringに空文字 "" を代入した場合、それはいわゆる「ゼロ値(zero value)」として扱われます。
しかし、Web APIを実装する以上、APIリクエストをしてくるクライアント側とのnull値の受け渡し、DBとのnull値の受け渡しは避けては通れません。
そこでGoでは、外部パッケージ gopkg.in/guregu/null.v1 を使うことで、Goでnullを許容できるstring型を扱うことが出来ます。
これは、
リクエストボディに、{"name" : null } のように、nullで来る可能性がある
DBに、name = NULL としてUPSERTしたい
DBから値を取得するとき、NULLの可能性がある
というようなケースの場合に有用です。
null.String型について
null.String型は以下のように定義されています。
type String struct {
sql.NullString
}
null.Stringは、内部に sql.NullString が埋め込まれています。
sql.NullStringはどういう型かというと、
type NullString struct {
String string
Valid bool // Valid is true if String is not NULL
}
上記のような型定義がされています。
実際の文字列としては String フィールドに値を持ち、 Validフィールドは、値があるかどうかをbooleanで表しています。
もし Valid = false であれば、その値はNULLである、という扱いです。
実装例
例えば、DBに空文字ではなく、NULLをUPSERTしたい場合は、以下のようにします。
type User struct {
name null.String
}
func main() {
user := User{
name: null.NewString("", false), // Valid = falseとすることでNULLに設定
}
db, err := sql.Open("mysql", "user:password@tcp(localhost:3306)/database")
if err != nil {
log.Fatal(err)
}
defer db.Close()
_, err = db.Exec(`UPDATE user SET name = ? WHERE id = ?`, user.name, 1)
if err != nil {
log.Fatal(err)
}
// UPDATE user SET name = NULL WHERE id = 1
}
null.NewString("", false)
にて、Valid = false とすることで、意図的にNULLを設定し、その値をクエリビルダーに渡すことで、NULLとして値をセットします。
逆に、以下のようにセットした場合、 name = '' 空文字として値がセットされてしまいます。
user := User{
name: null.StringFrom(""),
}
これは、null.StringFrom() の実装を確認してみると、
// StringFrom creates a new String that will never be blank.
func StringFrom(s string) String {
return NewString(s, true)
}
この関数でnull.Stringを生成した場合、デフォルトで Valid = true となってしまうので、空文字としてUPSERTされます。
したがって、NULLでUPSERTしたい場合は、明示的にnull.NewString() で第2引数にfalseを渡す必要があります。
以上で、null.Stringの説明をしてきました。
とても使いやすいパッケージ・関数であると共に、正しくNULLを扱うために、仕様をきちんと理解しておく必要があると感じました。
今回、「どのようにNULLを定義しているのか」を知ることができたのでよかったです。
皆さんもぜひ、きちんとNULLの扱い方を把握した上で使ってみてください。
TuneCoreJapanではエンジニアを積極採用中です!
音楽xITの業界に興味がある方、ぜひご応募ください。
この記事が気に入ったらサポートをしてみませんか?