GO语言程序查询数据库字段为空遇到的几个问题总结

如果字段值可能为空,那么从表里面读取数据的时候程序使用的变量类型应该使用 sql.NullXXX 类型,比如下面的日期类型:

1
2
3
4
5
var id uint
var createAt time.Time
var updateAt time.Time
var deleteAt sql.NullTime
var name string<br>var gartenId uint<br>var beginAt time.Time<br>var endAt time.Time<br>var monthBegin int<br>var monthEnd int<br>var child []byte<br>var content []byte<br>var creator uint<br>

 开始的时候,deleteAt 使用的也是 time.Time类型。

1
2
3
4
5
6
7
8
9
10
11
12
13
err = rows.Scan(&id,
            &createAt,
            &updateAt,
            &deleteAt,
            &name,
            &gartenId,
            &beginAt,
            &endAt,
            &monthBegin,
            &monthEnd,
            &child,
            &content,
            &creator)

 这里读取数据都没有问题,但是发现当数据库日期类型字段为NULL的时候,日期类型变量读取到的是0000年的默认日期值,如果稍后再用这个默认值插入数据库,会出现下面的错误:

incorrect datetime value: ‘0000-00-00‘ for column ‘start‘ at row 1

要解决这个问题的办法,就不能使用日期类型变量的默认值插入数据库,可以定义一个引用类型的变量,比如下面的代码,在上面Scan之后将读取出来的变量值赋值给一个结构对象。当然前提得定义变量为sql.NullXXX类型,比如下面代码中的 deleteAt变量:

1
2
3
4
5
6
7
8
9
var recipe entity.RecipeDO
 
recipe.ID = &id
recipe.CreateAt = &createAt
recipe.UpdateAt = &updateAt
if deleteAt.Valid {
    recipe.DeleteAt = &deleteAt.Time
}
recipe.Name = &name

 这样如果数据库字段值为空的话,deleteAt.Valid为假,那么 recipe.DeleteAt 字段就是空了(nil),下面看下 RecipeDO 结构体的定义:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
type RecipeDO struct {
    ID         *uint            
    CreateAt   *time.Time      
    UpdateAt   *time.Time       
    DeleteAt   *time.Time       
    Name       *string          
    GartenID   *uint            
    BeginAt    *time.Time       
    EndAt      *time.Time        
    MonthBegin *int              
    MonthEnd   *int             
    ChildList  *[]*ChildForRecipe
    Content    *[]*DailyMenu    
    Creator    *uint             
}

 以后插入数据的时候,判断下结构体字段 DeleteAt是否为空,写不同的插入代码即可,如下示例:

1
2
3
4
5
    if do.DeleteAt != nil {
    _, err = stmt.Exec(*do.UpdateAt, *do.DeleteAt, *do.Name, *do.GartenID, *do.BeginAt, *do.EndAt, *do.MonthBegin, *do.MonthEnd, jsChildList, jsContent, *do.ID)
}else{
    _, err = stmt.Exec(*do.UpdateAt, nil, *do.Name, *do.GartenID, *do.BeginAt, *do.EndAt, *do.MonthBegin, *do.MonthEnd, jsChildList, jsContent, *do.ID)
}

 当然也可以在上面的代码中Exec方法的第二个参数定义一个 sql.NullTime类型,就不用写上面的分支代码了。

PS:

GO语言程序查询数据处理空值的方式还是比较简陋的,容易掉坑里面去。要避免这个问题,最简单的办法还是在建表的时候,给所有字段都设置默认值。当然有时候字段值为NULL有特殊业务含义的话,上面的解决过程是绕不开了。

彩蛋:

上面示例中 RecipeDO 结构体的Content字段是一个复杂结构,数据库对应的表的Content字段是一个json类型,这个字段插入数据库之前必须先Json序列化,补上序列化它们的代码:

1
2
3
4
5
    jsContent,err1 :=json.Marshal(*do.Content)
if err1 != nil {
    logger.Errorf("Recipe update Content to JSON ", query, err1.Error())
    return  err1
}

 同样,从数据库读取这个字段,也要反序列化处理一下:

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
        err = rows.Scan(&id,
            &createAt,
            &updateAt,
            &deleteAt,
            &name,
            &gartenId,
            &beginAt,
            &endAt,
            &monthBegin,
            &monthEnd,
            &child,
            &content,
            &creator)
//其余代码略
var recipe entity.RecipeDO
//其余代码略
 
//反序列化content字段读取的值
var contentObj = make([]*entity.DailyMenu, 0)
        err = json.Unmarshal(content, &contentObj)
        if err != nil {
            logger.Errorf(" GetDO parse content error, ", query, err.Error())
            return nil, err
        }
        recipe.Content = &contentObj

 

参考链接:

go mysql null_Go 查询数据库 Scan Null 字段报错解决办法

https://blog.csdn.net/weixin_30940057/article/details/113566387?utm_medium=distribute.pc_relevant.none-task-blog-2~default~baidujs_title~default-0.no_search_link&spm=1001.2101.3001.4242

go语言scan空值报错

https://blog.csdn.net/leonpengweicn/article/details/51192557

解决Go语言数据库中null值的问题

https://www.jb51.net/article/202690.htm

 

posted on   深蓝医生  阅读(1281)  评论(1编辑  收藏  举报

编辑推荐:
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构

导航

< 2025年3月 >
23 24 25 26 27 28 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 1 2 3 4 5
点击右上角即可分享
微信分享提示