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