马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?立即注册
×
sql.NullString 和 sql.NullInt64 范例(以及其他类似的 sql.Null* 范例)在处置惩罚数据库利用时非常有用,尤其是在 Go 语言的 database/sql 包中。它们的告急用途包罗:
- 体现 NULL 值:
- 在数据库中,NULL 体现“没有值”。sql.Null* 范例允许你在 Go 语言中直接体现和处置惩罚这种“没有值”的状态,而不是使用 Go 的零值(如空字符串 "" 或整数 0)来体现 NULL,如许可以更精确地反映数据库中的数据状态。
- 数据库交互:
- 当你从数据库查询数据时,某些字段大概是 NULL。使用 sql.Null* 范例可以区分一个字段是真正包罗零值还是 NULL。这对于后续的逻辑处置惩罚非常告急,由于零值和 NULL 在逻辑上大概有差异的寄义。
- 克制错误:
- 假如不使用 sql.Null* 范例,你大概会错误地将数据库中的 NULL 值表明为 Go 语言中的零值,这大概导致逻辑错误或步调瓦解。
- 简化代码:
- 使用 sql.Null* 范例可以简化代码,由于你不必要编写额外的查抄来确定一个值是否来自数据库中的 NULL。Valid 字段可以直接告诉你这个值是否有用。
- 数据完备性:
- 在将数据写入数据库时,sql.Null* 范例可以资助你保持数据的完备性。你可以精确地标志哪些字段是故意设置为 NULL,哪些字段是具有实际值的。
- 机动性:
- sql.Null* 范例提供了机动性,允许你在不改变现有代码布局的环境下,处置惩罚那些大概为 NULL 的字段。
示例场景:
假设你正在处置惩罚一个用户信息表,此中有一个字段体现用户的出生日期。在某些环境下,用户大概没有提供出生日期,因此这个字段在数据库中是 NULL。使用 sql.NullString 或 sql.NullInt64(取决于出生日期怎样存储)可以让你在 Go 步调中精确地体现这种环境,并在必要时举行恰当的处置惩罚。- package main
- import (
- "database/sql"
- "fmt"
- )
- // DbBackedUser 用户结构体,由数据库支持
- type DbBackedUser struct {
- Name sql.NullString
- Age sql.NullInt64
- }
- func main() {
- // 空值
- var name sql.NullString
- x := DbBackedUser{Name: name, Age: sql.NullInt64{Int64: 0, Valid: false}}
- // 输出结果
- fmt.Println("Name:")
- fmt.Println("String:", x.Name.String) // 输出: ""
- fmt.Println("Valid:", x.Name.Valid) // 输出: false
- fmt.Println("Age:")
- fmt.Println("Int64:", x.Age.Int64) // 输出: 0
- fmt.Println("Valid:", x.Age.Valid) // 输出: false
- }
复制代码- Name:
- String:
- Valid: false
- Age:
- Int64: 0
- Valid: false
复制代码 到场与数据库交互
1. 创建测试表
clickhouse-client
起首,我们必要在 ClickHouse 中创建一个测试表。由于你的数据布局包罗 Name 和 Age,我们可以使用以下 SQL 语句创建表: - CREATE TABLE test_table
- (
- `Name` Nullable(String),
- `Age` Nullable(Int64)
- ) ENGINE = Memory;
复制代码 2. 插入数据
接下来,我们插入一些数据,包罗空值和零值:- INSERT INTO test_table VALUES ('Kimi', 30), ('', 0), (NULL, NULL);
复制代码 这里,我们插入了三行数据:
- 第一行:Name 是 "Kimi",Age 是 30。
- 第二行:Name 是空字符串,Age 是 0。
- 第三行:Name 和 Age 都是 NULL。
3. 查询数据
查询表中的全部数据:- SELECT * FROM test_table;
复制代码 - package main
- import (
- "database/sql"
- "fmt"
- "log"
- _ "github.com/ClickHouse/clickhouse-go"
- )
- // DbBackedUser 用户结构体,由数据库支持
- type DbBackedUser struct {
- Name sql.NullString
- Age sql.NullInt64
- }
- func main() {
- // 连接到 ClickHouse 数据库
- connStr := "tcp://localhost:9000?username=default&password=&database=default"
- db, err := sql.Open("clickhouse", connStr)
- if err != nil {
- log.Fatal(err)
- }
- defer db.Close()
- // 查询并打印现有的数据
- fmt.Println("Query results before insertion:")
- queryData(db)
- fmt.Println("查询并打印现有的数据")
- // 开始事务
- tx, err := db.Begin()
- if err != nil {
- log.Fatal(err)
- }
- // 空值
- var name sql.NullString
- x := DbBackedUser{Name: name, Age: sql.NullInt64{Int64: 1, Valid: false}}
- // 插入数据
- _, err = tx.Exec("INSERT INTO test_table2 (Name, Age) VALUES (?, ?)", x.Name, x.Age)
- if err != nil {
- tx.Rollback() // 如果出现错误,回滚事务
- log.Fatal(err)
- }
- // 提交事务
- err = tx.Commit()
- if err != nil {
- log.Fatal(err)
- }
- // 查询并打印插入后的数据
- fmt.Println("Query results after insertion:")
- queryData(db)
- fmt.Println("查询并打印现有的数据")
- }
- func queryData(db *sql.DB) {
- rows, err := db.Query("SELECT Name, Age FROM test_table2")
- if err != nil {
- log.Fatal(err)
- }
- defer rows.Close()
- var nameValue sql.NullString
- var ageValue sql.NullInt64
- for rows.Next() {
- if err := rows.Scan(&nameValue, &ageValue); err != nil {
- log.Fatal(err)
- }
- fmt.Printf("name: %v, Age: %v\n", nameValue.String, ageValue.Int64)
- }
- }
复制代码 效果

这里
给数据库传了0值
改成true后

 传值乐成

NameValid改成false

传null
 - package main
- import (
- "database/sql"
- "fmt"
- "reflect"
- "github.com/go-playground/validator/v10"
- )
- // DbBackedUser 用户结构体,由数据库支持
- type DbBackedUser struct {
- Name sql.NullString `validate:"required"` // 可以为NULL的字符串,验证规则为必填
- Age sql.NullInt64 `validate:"required"` // 可以为NULL的整数,验证规则为必填
- }
- // 使用单一实例的验证器,它缓存结构体信息
- var validate *validator.Validate
- func main() {
- // 创建一个新的验证器实例
- validate = validator.New()
- // 注册所有sql.Null*类型,使用自定义的ValidateValuer函数
- validate.RegisterCustomTypeFunc(ValidateValuer, sql.NullString{}, sql.NullInt64{})
- // 构建对象进行验证
- // Name字段为空字符串但Valid为true,表示非NULL;Age字段为0且Valid为false,表示NULL
- x := DbBackedUser{Name: sql.NullString{String: "zahngsan", Valid: true}, Age: sql.NullInt64{Int64: 18, Valid: false}}
- // 对x进行结构体验证
- err := validate.Struct(x)
- // 如果验证失败,打印错误信息
- if err != nil {
- fmt.Printf("Err(s):\n%+v\n", err)
- }
- }
- // ValidateValuer实现了validator.CustomTypeFunc接口
- func ValidateValuer(field reflect.Value) interface{} {
- // 检查field是否为nil
- if !field.IsValid() {
- return nil
- }
- // 检查field是否实现了sql.Null*类型
- if nullType, ok := field.Interface().(sql.NullString); ok {
- // 如果Valid为true,返回字符串和非NULL的指示
- if nullType.Valid {
- fmt.Println("NullString Valid为true:",nullType.String)
- return []interface{}{nullType.String, true}
- }
- // 如果Valid为false,返回空字符串和NULL的指示
- fmt.Println("NullString Valid为false")
- return []interface{}{"", false}
- }
- // 检查field是否实现了sql.NullInt64类型
- if nullInt64, ok := field.Interface().(sql.NullInt64); ok {
- if nullInt64.Valid {
- fmt.Println("NullInt64 Valid为true:",nullInt64.Int64)
- return []interface{}{nullInt64.Int64,true}
- }
- fmt.Println("NullInt64 Valid为false")
- return []interface{}{"", false}
- }
- // 对其他sql.Null*类型进行类似的处理...
- // ...
- // 如果不是sql.Null*类型,返回nil
- return nil
- }
复制代码 效果

|