日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

由 go orm 引发的探索

發(fā)布時(shí)間:2024/8/23 编程问答 25 豆豆
生活随笔 收集整理的這篇文章主要介紹了 由 go orm 引发的探索 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

前言

今天遇到了一個(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)該是:

  • 在Scan方法內(nèi)打斷點(diǎn), 查看是否調(diào)用了方法以及兩次調(diào)用傳的參數(shù)是否一致
  • 當(dāng)發(fā)現(xiàn)調(diào)用方法且參數(shù)一致時(shí), 就直接到了最后一步并最終找到指針的問題
  • 若沒有調(diào)用方法或參數(shù)不一致時(shí), 再往調(diào)用的地方去找
  • 步驟簡單來說, 就是自上而下, 先從外層找問題, 當(dāng)發(fā)現(xiàn)外層一切正常, 再向里邊找, 就像剝洋蔥一樣, 一層一層, 直到定位到問題所在.

    總結(jié)

    以上是生活随笔為你收集整理的由 go orm 引发的探索的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。