not nullが指定されていないテーブルがあった場合の話。
CREATE TABLE "users" (
"id" serial not null primary key,
"name" text,
"age" integer
)
gorpで単純にinsertすると、ゼロ値が入っていまいます。
dbmap.Insert(
&User{Name: "John", Age: 0}, // insert into "users" ("id","name","age") values (default,$1,$2) returning Id; [1:"John" 2:0]
&User{Name: "John"}, // insert into "users" ("id","name","age") values (default,$1,$2) returning Id; [1:"John" 2:0]
&User{Name: "", Age: 8}, // insert into "users" ("id","name","age") values (default,$1,$2) returning Id; [1:"" 2:8]
&User{Age: 30}, // insert into "users" ("id","name","age") values (default,$1,$2) returning Id; [1:"" 2:30]
)
go言語はゼロ値があるので、空文字なのかNULL
なのかが判別できないのが原因です。
これに対応するために、database/sql
パッケージには、null
値を扱うために
sql.NullString
やsql.NullBool
などが用意されています。
使い方
nullの可能性のある型にsql.NullString
などを指定。
type User struct {
Id int `db:id`
Name sql.NullString `db:name`
Age sql.NullInt64 `db:age`
}
このsql.NullString
などは、こんな構造体になってます。
type NullString struct {
String string
Valid bool // Valid is true if String is not NULL
}
Insertを呼び出す前に、Valid
の値をfalse
にするとnull
としてINSERTされるようになります。
dbmap.Insert(
&User{
Name: sql.NullString{"Mike", true},
Age: sql.NullInt64{0, true},
}, // insert into "users" ("id","name","age") values (default,$1,$2) returning Id; [1:"Mike" 2:0]
&User{
Name: sql.NullString{"", false},
Age: sql.NullInt64{30, true},
}, // insert into "users" ("id","name","age") values (default,$1,$2) returning Id; [1:<nil> 2:30]
)
取り出すときもこのValid
の値をみることになります。
dbmap.Insert(
&User{Name: sql.NullString{"Mike", true}, Age: sql.NullInt64{0, true}},
&User{Name: sql.NullString{"John", true}, Age: sql.NullInt64{0, false}},
&User{Name: sql.NullString{"John", true}, Age: sql.NullInt64{8, true}},
&User{Name: sql.NullString{"", false}, Age: sql.NullInt64{30, true}},
)
users := []User{}
_, err := dbmap.Select(&users, "select * from users")
if err != nil {
log.Fatal(err)
}
for _, user := range users {
spew.Dump(user)
}
/*
(main.User) {
Id: (int) 1,
Name: (sql.NullString) {
String: (string) (len=4) "Mike",
Valid: (bool) true
},
Age: (sql.NullInt64) {
Int64: (int64) 0,
Valid: (bool) true
}
}
(main.User) {
Id: (int) 2,
Name: (sql.NullString) {
String: (string) (len=4) "John",
Valid: (bool) true
},
Age: (sql.NullInt64) {
Int64: (int64) 0,
Valid: (bool) false
}
}
(main.User) {
Id: (int) 3,
Name: (sql.NullString) {
String: (string) (len=4) "John",
Valid: (bool) true
},
Age: (sql.NullInt64) {
Int64: (int64) 8,
Valid: (bool) true
}
}
(main.User) {
Id: (int) 4,
Name: (sql.NullString) {
String: (string) "",
Valid: (bool) false
},
Age: (sql.NullInt64) {
Int64: (int64) 30,
Valid: (bool) true
}
}
*/
ちなみにこれ、普通にScanをするときも使えます。
var s NullString
err := db.QueryRow("SELECT name FROM foo WHERE id=?", id).Scan(&s)
...
if s.Valid {
// use s.String
} else {
// NULL value
}