由 go orm 引发的探索
前言
今天遇到了一個(gè) bug, 是 golang 的orm導(dǎo)致的. 使用了gorm框架. 通過實(shí)現(xiàn)Scan與Value可以將數(shù)據(jù)庫中的 json 內(nèi)容解析出來, 免除了 字符串再解碼的步驟. 當(dāng)時(shí)報(bào)錯(cuò)的代碼大概是這樣的:
type TestContent struct {Id intContent Content // 數(shù)據(jù)庫中的 json 結(jié)構(gòu) }type Content struct {Name stringAge int }func (c *Content) Scan(value interface{}) error {return json.Unmarshal(value.([]byte), c) }func (c *Content) Value() (driver.Value, error) {return json.Marshal(c) }向數(shù)據(jù)庫插入數(shù)據(jù), 調(diào)用Create方法時(shí)報(bào)錯(cuò)了:
[2020-08-28 23:18:25] sql: converting argument $1 type: unsupported type main.Content, a struct
這這這, 什么鬼? 當(dāng)時(shí)我百思不得其所. 經(jīng)過多次嘗試, 我發(fā)現(xiàn)將Value方法的從屬從指針類型改為值類型就可以解決這個(gè)問題.
此時(shí)我恍然大悟, 想起了之前的方法集的概念.
也就是說, go 在底層是使用值類型來調(diào)用的, 所以拿不到指針方法, 故而報(bào)錯(cuò).
看到這里, 如果你也遇到同樣的問題, 將Value方法從屬改為值類型就可以解決了. 以下內(nèi)容是我手賤之后的另一個(gè)愚蠢記錄, 可跳過.
另一個(gè)問題
此時(shí)我以為我已經(jīng)深得精髓, 解決方法很簡單, 將兩個(gè)方法的從屬都改為值類型就好了嘛. 修改后, 插入數(shù)據(jù)果然沒有問題了, 但是當(dāng)我查詢的時(shí)候, 發(fā)現(xiàn)了另一個(gè)問題,?Content對象沒有賦值, 是空的.
當(dāng)時(shí)我一臉懵逼, 沒有找到問題所在, 我做了什么? 于是, 我就開始了打斷點(diǎn)之路:
我發(fā)現(xiàn)它走到這里, 調(diào)用了Scan方法, 那么, dest 又是個(gè)什么對象呢?
于是, 我又找到了這個(gè)賦值的地方, 將類型打印出來后, 是:
**main.Content
是一個(gè)二級指針, 這時(shí), 我以為是因?yàn)槎壷羔樀膯栴}. 于是我動(dòng)手寫了一段代碼來模擬這段操作:
func main(){// 這里模擬了當(dāng)時(shí)設(shè)置的代碼內(nèi)容typeOf := reflect.TypeOf(Content{})reflectValue := reflect.New(reflect.PtrTo(typeOf))reflectValue.Elem().Set(reflect.ValueOf(&Content{}))r := reflectValue.Interface()if c, ok := r.(**Content); ok {(**c).SetName("1111")fmt.Println(fmt.Sprintf("%+v", **c))} }// 這里, 為了方便測試, 添加了 SetName 方法, 與 Scan 相同 func (nt Content) SetName(name string) {nt.Name = name }當(dāng)我看到結(jié)果的時(shí)候, 發(fā)現(xiàn)name依舊沒有設(shè)置進(jìn)去. 我了個(gè)喵, 什么情況?
然后我開始了瘋狂檢查的過程, 直到我寫下了這段代碼之后, 我陷入了沉思:
content := Content{}content.SetName("hh")fmt.Println(fmt.Sprintf("%+v", content))當(dāng)我發(fā)現(xiàn)直接設(shè)置都沒用的時(shí)候, 我知道, 一定是我哪個(gè)最簡單的地方出錯(cuò)了. 我默默的點(diǎn)起一支煙, 望著眼前的代碼發(fā)起了呆.
我經(jīng)過與之前改動(dòng)的對比, 知道問題一定是出在指針與值類型的轉(zhuǎn)換上.
我我我我的天, 最終我發(fā)現(xiàn)我犯了一個(gè)多么愚蠢的錯(cuò)誤. 使用值類型是無法對其字段進(jìn)行修改的, 其修改通通是通過值復(fù)制進(jìn)行, 并不會(huì)影響原始對象. 而且我右打了斷點(diǎn)發(fā)現(xiàn), 方法并不是沒有調(diào), 確實(shí)是調(diào)用了, 只不過因?yàn)閺膶倥c值而沒有對原始對象造成影響.
總結(jié)
就在我剛開始查這個(gè)問題的時(shí)候, 我自認(rèn)為找到了什么不得了的 bug, 滿心激動(dòng)的查了下去. 直到最終發(fā)現(xiàn)問題的時(shí)候, 我懵逼了.
之前我哥就和我說, 查問題要從表現(xiàn)去推測. 而這次就是直接奔著底層去了, 結(jié)果做了很多無用功.
我回想了一下, 當(dāng)時(shí)正確的檢查步驟應(yīng)該是:
步驟簡單來說, 就是自上而下, 先從外層找問題, 當(dāng)發(fā)現(xiàn)外層一切正常, 再向里邊找, 就像剝洋蔥一樣, 一層一層, 直到定位到問題所在.
總結(jié)
以上是生活随笔為你收集整理的由 go orm 引发的探索的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Wordpress不同页面显示不同小工具
- 下一篇: 为什么同大取大同小取小_不锈钢马大型动物