golang是强类型语言,在赋值和解析过程中需要先定义好数据类型,否在会报类型错误,下面总结在处理数据库表时遇到字段为空或零值的情况
场景
假如存在如下没有指定not null的场合
1
2
3
4
5
|
CREATE TABLE "users" (
"id" serial not null primary key,
"name" text,
"age" integer
)
|
在gorp中insert插入场合,可以直接赋零值即可,很方便.
1
2
3
4
5
6
|
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]
)
|
在使用golang中零值与空值和NULL是不同的数据类型和值,需要加以判断,在数据库表中的NULL值字段可以用database/sql数据包中提供的sql.NullString,sql.NullBool等值类型进行判断后加以使用.
如何使用
可能存在NULL值的数据类型可以使用sql.NullString或sql.NullBool等来指定其类型.
1
2
3
4
5
|
type User struct{
Id int `db:id`
Name sql.NullString `db:name`
Age sql.NullInt64 `db:age`
}
|
其中sql.NullString,它的结构如下:
1
2
3
4
|
type NullString struct {
String string
Valid bool // Valid is true if String is not NULL
}
|
借助.sql.NullString这样的结构体就可以在insert时,通过设置Valid的值为fasle就可以表示此值为null值,这样在读取时如果为false就可以肯定此值为默认的空值了,
具体操作如下:
1
2
3
4
5
6
7
8
9
10
|
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的值判断是否为设置的零值还是未被赋值操作.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
|
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
}
}
*/
|
在单次查询中根据valid的值作相应的零值判断处理, 方法如下:
1
2
3
4
5
6
7
8
|
var s NullString
err := db.QueryRow("SELECT name FROM foo WHERE id=?", id).Scan(&s)
...
if s.Valid {
// use s.String
} else {
// NULL value
}
|
空值处理
数据库有一个特殊的类型,NULL空值。可是NULL不能通过scan直接跟普遍变量赋值,甚至也不能将null赋值给nil。对于null必须指定特殊的类型,这些类型定义在database/sql库中。例如sql.NullFloat64。如果在标准库中找不到匹配的类型,可以尝试在驱动中寻找。下面是一个简单的例子:
1
2
3
4
5
6
7
8
9
10
|
var (
s1 string
s2 sql.NullString
i1 int
f1 float64
f2 float64
)
// 假设数据库的记录为 ["hello", NULL, 12345, "12345.6789", "not-a-float"]
err = rows.Scan(&s1, &s2, &i1, &f1, &f2) if err != nil {
log.Fatal(err) }
|
因为最后一个f2字段的值不是float,这会引发一个错误。
sql: Scan error on column index 4: converting string "not-a- oat" to a oat64: strconv.ParseFloat: parsing "not-a- oat": invalid syntax
如果忽略err,强行读取目标变量,可以看到最后一个值转换错误会处理,而不是抛出异常:
1
2
3
4
|
err = rows.Scan(&s1, &s2, &i1, &f1, &f2)
log.Printf("%q %#v %d %f %f", s1, s2, i1, f1, f2)
// 输出 "hello" sql.NullString{String:"", Valid:false} 12345 12345.678900 0.000000
|
可以看到,除了最后一个转换失败变成了零值之外,其他都正常的取出了值,尤其是null匹配了NullString类型的目标变量。
对于null的操作,通常仍然需要验证:
1
2
3
4
5
6
7
8
9
|
var world sql.NullString
err := db.QueryRow("SELECT world FROM hello WHERE id = ?", id).Scan(&world)
...
if world.Valid {
wrold.String
} else {
// 数据库的value是不是null的时候,输出 world的字符串值, 空字符串
world.String
}
|
对应的,如果world字段是一个int,那么声明的目标变量类似是sql.NullInt64,读取其值的方法为world.Int64。
但是有时候我们并不关心值是不是Null,我们只需要把他当一个空字符串来对待就行。这时候我们可以使用[]byte(null byte[]可以转化为空string)
1
2
3
4
|
var world []byte
err := db.QueryRow("SELECT world FROM hello WHERE id = ?", id).Scan(&world)
...
log.Println(string(real_name)) // 有值则取出字串值,null则转换成 空字串。
|