如果字段值可能為空,那么從表里面讀取數據的時候程序使用的變量類型應該使用 sql.NullXXX 類型,比如下面的日期類型:
var id uint var createAt time.Time var updateAt time.Time var deleteAt sql.NullTime var name string
var gartenId uint
var beginAt time.Time
var endAt time.Time
var monthBegin int
var monthEnd int
var child []byte
var content []byte
var creator uint
開始的時候,deleteAt 使用的也是 time.Time類型。
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變量:
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 結構體的定義:
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是否為空,寫不同的插入代碼即可,如下示例:
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序列化,補上序列化它們的代碼:
jsContent,err1 :=json.Marshal(*do.Content) if err1 != nil { logger.Errorf("Recipe update Content to JSON ", query, err1.Error()) return err1 }
同樣,從數據庫讀取這個字段,也要反序列化處理一下:
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