細(xì)數(shù)在用golang&beego做api server過程中的坑(二)

在介紹之前先說明一下,標(biāo)題中帶有【beego】標(biāo)簽的暂论,是beego框架使用中遇到的坑面褐。如果沒有,那就是golang本身的坑取胎。當(dāng)然展哭,此坑并非人家代碼有問題,有幾個(gè)地方反而是出于性能等各方面的考量有意而為之闻蛀。但這些地方卻是一門語言或框架的初學(xué)者大概率會(huì)遇到的困惑匪傍。
傳送門:細(xì)數(shù)在用golang&beego做api server過程中的坑(一)

【beego】4、orm查詢時(shí)沒有對(duì)應(yīng)結(jié)果集: now row found (error)

java程序員對(duì)于持久層框架可能都用過mybatis觉痛,對(duì)查詢操作也會(huì)比較熟悉役衡。當(dāng)一行代碼下去,數(shù)據(jù)庫給你咔咔一頓找薪棒,發(fā)現(xiàn)沒有結(jié)果時(shí)手蝎,會(huì)返回null 或 一個(gè)空的collection。beego中也有持久層框架俐芯,就叫orm棵介。對(duì)于查詢方面的處理,如果數(shù)據(jù)庫查詢后沒有找到任何結(jié)果吧史,orm不會(huì)返回空邮辽,而是會(huì)返回一個(gè)error!贸营!
舉例:

var container po.OnlineAccumulateRecord
    err = o.QueryTable(new(po.OnlineAccumulateRecord)).Filter("User", user.Id).One(&container)
    if err != nil && !models.IsNoRowFoundError(err) {
        return 0, err
    }

這段代碼是我從現(xiàn)有工程里面隨便摘出來的吨述,大意就是通過用戶id篩選出積分表里的一條記錄。這個(gè)時(shí)候如果查不到對(duì)應(yīng)的記錄莽使,err != nil 便會(huì)成立锐极。這時(shí)候單純的通過 err !=nil就無法判斷到底是真的出現(xiàn)數(shù)據(jù)庫查詢錯(cuò)誤,還是沒有找到對(duì)應(yīng)記錄芳肌。因此就有了models.IsNoRowFoundError(err)這一判斷灵再。 對(duì)于是否是NoRowFoundError,可行的方法之一是判斷err是否相等:

if err == orm.ErrNoRows {
}

之二是判斷error message:

//判斷一個(gè)錯(cuò)誤是不是因?yàn)閿?shù)據(jù)庫中沒有符合條件的記錄亿笤,而拋出的錯(cuò)誤翎迁。
//這種情況是一種正常的情況,不應(yīng)該作為錯(cuò)誤拋出
func IsNoRowFoundError(err error) bool {
    if err == nil {
        return false
    }

    if strings.Contains(err.Error(), "no row found") {
        return true
    }
    return false
}

當(dāng)然對(duì)于其他的查詢方式净薛,也會(huì)有這個(gè)問題汪榔,例如:

o := orm.NewOrm()
err = o.Read(&orderInfo, "OuterOrderId")

在此就不一一列舉。
我個(gè)人認(rèn)為astaxie大神在設(shè)計(jì)beego orm時(shí)不可能沒考慮到這一點(diǎn)肃拜。之所以這樣設(shè)計(jì)痴腌,可能的一個(gè)原因是雌团,對(duì)于剛才的查詢代碼:

var container po.OnlineAccumulateRecord
 err = o.QueryTable(new(po.OnlineAccumulateRecord)).Filter("User", user.Id).One(&container)

不能夠通過判斷container是不是nil來斷定有沒有查詢到對(duì)應(yīng)的記錄。因?yàn)樵趃olang中士聪,一個(gè)變量由兩部分組成:type & value锦援,只要任何一部分是明確的,則該變量就不是nil剥悟。在上例中灵寺,container的type已經(jīng)明確聲明是po.OnlineAccumulateRecord, 此時(shí)value是零值。因此它永遠(yuǎn)都不會(huì)是nil区岗。

【beego】5略板、orm插入記錄,如果pk是非整型(e.g. string):no LastInsertId available error

orm設(shè)計(jì)時(shí)對(duì)于整型pk做了特別的支持:

當(dāng) Field 類型為 int, int32, int64, uint, uint32, uint64 時(shí)慈缔,可以設(shè)置字段為自增健
當(dāng)模型定義里沒有主鍵時(shí)叮称,符合上述類型且名稱為 Id 的 Field 將被視為自增健。

但如果主鍵是string胀糜,通過以下代碼做insert操作時(shí)會(huì)返回"no LastInsertId available" error颅拦。因此需要增加IsNoLastInsertIdError判斷以免影響程序運(yùn)行的分支走向。

    o := orm.NewOrm()
    _, err = o.Insert(wechatNotify)
    if models.IsNoLastInsertIdError(err) {
        return nil
    }

同樣的教藻,IsNoLastInsertIdError判斷的方法之一是根據(jù)error message判斷:

//這種情況是一種正常的情況距帅,不應(yīng)該作為錯(cuò)誤拋出
func IsNoLastInsertIdError(err error) bool {
    if err == nil {
        return false
    }

    if strings.Contains(err.Error(), "no LastInsertId available") {
        return true
    }

    return false
}

【beego】6、報(bào)錯(cuò)誤導(dǎo):<Ormer> table: '.' not found,

好不容易把代碼擼完了括堤,興沖沖的運(yùn)行debug碌秸,結(jié)果發(fā)現(xiàn)如下報(bào)錯(cuò)信息:

<Ormer> table: `.` not found, make sure it was registered with `RegisterModel()`

根據(jù)字面意思是說正在操作的這個(gè)名字為“.”的table沒有進(jìn)行regist操作。于是排查悄窃,但很快發(fā)現(xiàn):
1讥电、根本就沒有一個(gè)叫"."的table
2、正在操作的表也已經(jīng)在beego啟動(dòng)的時(shí)候完成了regist操作
然后通過源碼進(jìn)行定位轧抗,報(bào)錯(cuò)位置:


func (o *orm) getMiInd(md interface{}, needPtr bool) (mi *modelInfo, ind reflect.Value) {
    val := reflect.ValueOf(md)
    ind = reflect.Indirect(val)
    typ := ind.Type()
    if needPtr && val.Kind() != reflect.Ptr {
        panic(fmt.Errorf("<Ormer> cannot use non-ptr model struct `%s`", getFullName(typ)))
    }
    name := getFullName(typ)
    if mi, ok := modelCache.getByFullName(name); ok {
        return mi, ind
    }
    panic(fmt.Errorf("<Ormer> table: `%s` not found, make sure it was registered with `RegisterModel()`", name))
}

那這個(gè)name是怎么得出來的呢恩敌?

// get reflect.Type name with package path.
func getFullName(typ reflect.Type) string {
    return typ.PkgPath() + "." + typ.Name()
}

也就是說,type.PkgPath() 和 type.Name()都是empty string 返回值横媚。那什么時(shí)候他們返回的是empty string 呢纠炮?

    // Name returns the type's name within its package for a defined type.
    // For other (non-defined) types it returns the empty string.
    Name() string

    // PkgPath returns a defined type's package path, that is, the import path
    // that uniquely identifies the package, such as "encoding/base64".
    // If the type was predeclared (string, error) or not defined (*T, struct{},
    // []int, or A where A is an alias for a non-defined type), the package path
    // will be the empty string.
    PkgPath() string

從上述代碼可以看出,當(dāng)o.Insert()接收到參數(shù)是not defined的話灯蝴,就會(huì)導(dǎo)致該項(xiàng)錯(cuò)誤恢口,而并不是說這個(gè)玩意沒有regist(就這些玩意咋去regist嘛)。 注釋部分也說了not defined包括哪幾種:*T, struct{}, []int穷躁,以及這幾類的別名耕肩。
最后舉兩個(gè)能出現(xiàn)這個(gè)問題的例子:

//example 1
_struct := new(structExample)
o := orm.NewOrm()
o.Insert(&_pointer)

這里要特別說明golang里面new的用法:

// The new built-in function allocates memory. The first argument is a type,
// not a value, and the value returned is a pointer to a newly
// allocated zero value of that type.
func new(Type) *Type

注釋很明確的提到,new返回的是一個(gè)指針。更淺顯的犯錯(cuò)方式就像example2這樣:

//example 2
var _struct structExample
var _pointer &_struct
o := orm.NewOrm()
o.Insert(&_pointer)

【beego】7猿诸、報(bào)錯(cuò)誤導(dǎo):panic: reflect: call of reflect.Value.Interface on zero Value

再一次好不容易把代碼擼完了婚被,興沖沖的運(yùn)行debug,結(jié)果發(fā)現(xiàn)如下報(bào)錯(cuò)信息:

panic: reflect: call of reflect.Value.Interface on zero Value
goroutine 1 [running]:
reflect.valueInterface(0x0, 0x0, 0x0, 0x1, 0x0, 0x0)
    /usr/local/Cellar/go/1.11/libexec/src/reflect/value.go:953 +0x2ce
reflect.Value.Interface(0x0, 0x0, 0x0, 0x0, 0x0)
    /usr/local/Cellar/go/1.11/libexec/src/reflect/value.go:948 +0x4c
github.com/astaxie/beego/orm.getFieldType(0x1af8900, 0xc0001e19e0, 0x196, 0x0, 0x0, 0x0)
    /Users/tangxqa/develop/code/go/src/github.com/astaxie/beego/orm/models_utils.go:181 +0xd00
github.com/astaxie/beego/orm.newFieldInfo(0xc0001f9040, 0x1af8900, 0xc0001e19e0, 0x196, 0x1a873ee, 0x4, 0x0, 0x0, 0x1e2bba0, 0x1af8900, ...)
    /Users/tangxqa/develop/code/go/src/github.com/astaxie/beego/orm/models_info_f.go:244 +0x372e
github.com/astaxie/beego/orm.addModelFields(0xc0001f9040, 0x1bf8820, 0xc0001e1980, 0x199, 0x0, 0x0, 0xc0000dd828, 0x0, 0x0)
    /Users/tangxqa/develop/code/go/src/github.com/astaxie/beego/orm/models_info_m.go:70 +0x3f4
github.com/astaxie/beego/orm.newModelInfo(0x1af8340, 0xc0001e1980, 0x16, 0xc0001f9040)
    /Users/tangxqa/develop/code/go/src/github.com/astaxie/beego/orm/models_info_m.go:45 +0x302
github.com/astaxie/beego/orm.registerModel(0x1c4b231, 0x4, 0x1af8340, 0xc0001e1980, 0x201)
    /Users/tangxqa/develop/code/go/src/github.com/astaxie/beego/orm/models_boot.go:62 +0x7b0
github.com/astaxie/beego/orm.RegisterModelWithPrefix(0x1c4b231, 0x4, 0xc0000ddd38, 0x7, 0x7)
    /Users/tangxqa/develop/code/go/src/github.com/astaxie/beego/orm/models_boot.go:320 +0x16d
rrs.com/rrsservice/models/po.init.28()
    /Users/tangxqa/develop/code/go/src/rrs.com/rrsservice/models/po/payment_wx.go:147 +0x2aa

可以通過堆棧信息看出這一報(bào)錯(cuò)發(fā)生在regist model時(shí)两芳,regist部分的代碼為:

    orm.RegisterModelWithPrefix(prefix, new(PaymentInfo))

PaymentInfo:


type PaymentInfo struct {
    Id              string                `orm:"size(32);pk;column(id)"`
    Status          string                
    OrderId         string                
    OrderSource     string               
    PayType         string               
    OrderPrice      int                   
    UnifiedOrderRes *UnifiedOrderResponse `orm:"rel(fk);null"` 
    User            *User                       
    Ts              time.Time            
}

好像代碼寫的沒毛病摔寨。只好再根據(jù)堆棧信息找到beego源碼中:github.com/astaxie/beego/orm.getFieldType 位于 astaxie/beego/orm/models_utils.go:181getFieldType方法中是一堆類型判斷代碼:

// return field type as type constant from reflect.Value
func getFieldType(val reflect.Value) (ft int, err error) {
    switch val.Type() {
    case reflect.TypeOf(new(int8)):
        ft = TypeBitField
    case reflect.TypeOf(new(int16)):
        ft = TypeSmallIntegerField
    case reflect.TypeOf(new(int32)),
        reflect.TypeOf(new(int)):
        ft = TypeIntegerField
    case reflect.TypeOf(new(int64)):
        ft = TypeBigIntegerField
···
···

什么時(shí)候會(huì)走到這怖辆?那就看看github.com/astaxie/beego/orm.newFieldInfo 位于 gthub.com/astaxie/beego/orm/models_info_f.go:244的代碼:
這些代碼都是一些對(duì) 字段tag 的分析處理。等等删顶,是不是忘記添加tag了JΑ!

tag = "rel"
        tagValue = tags[tag]
        if tagValue != "" {
            switch tagValue {
            case "fk":
                fieldType = RelForeignKey
                break checkType
            case "one":
                fieldType = RelOneToOne
                break checkType
            case "m2m":
                fieldType = RelManyToMany
                if tv := tags["rel_table"]; tv != "" {
                    fi.relTable = tv
                } else if tv := tags["rel_through"]; tv != "" {
                    fi.relThrough = tv
                }
                break checkType
            default:
                err = fmt.Errorf("rel only allow these value: fk, one, m2m")
                goto wrongTag
            }
        }
        tag = "reverse"
        tagValue = tags[tag]
        if tagValue != "" {
            switch tagValue {
            case "one":
                fieldType = RelReverseOne
                break checkType
            case "many":
                fieldType = RelReverseMany
                if tv := tags["rel_table"]; tv != "" {
                    fi.relTable = tv
                } else if tv := tags["rel_through"]; tv != "" {
                    fi.relThrough = tv
                }
                break checkType
            default:
                err = fmt.Errorf("reverse only allow these value: one, many")
                goto wrongTag
            }
        }

        fieldType, err = getFieldType(addrField)

添加tag逗余,搞定L嘏亍!

   User            *User                `orm:"rel(fk)"`       

那是不是所有的字段在orm時(shí)都需要添加標(biāo)簽录粱? 不是腻格。當(dāng)字段是指針類型時(shí),如果沒有用orm:"-"進(jìn)行orm忽略啥繁,必須要添加標(biāo)簽來進(jìn)行表關(guān)系設(shè)置菜职。
相關(guān)資料:https://beego.me/docs/mvc/model/models.md 參照其中的表關(guān)系設(shè)置章節(jié)。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末旗闽,一起剝皮案震驚了整個(gè)濱河市酬核,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌适室,老刑警劉巖嫡意,帶你破解...
    沈念sama閱讀 212,454評(píng)論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異捣辆,居然都是意外死亡蔬螟,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,553評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門汽畴,熙熙樓的掌柜王于貴愁眉苦臉地迎上來旧巾,“玉大人,你說我怎么就攤上這事整袁〔こ荩” “怎么了?”我有些...
    開封第一講書人閱讀 157,921評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵坐昙,是天一觀的道長绳匀。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么疾棵? 我笑而不...
    開封第一講書人閱讀 56,648評(píng)論 1 284
  • 正文 為了忘掉前任戈钢,我火速辦了婚禮,結(jié)果婚禮上是尔,老公的妹妹穿的比我還像新娘殉了。我一直安慰自己,他們只是感情好拟枚,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,770評(píng)論 6 386
  • 文/花漫 我一把揭開白布薪铜。 她就那樣靜靜地躺著,像睡著了一般恩溅。 火紅的嫁衣襯著肌膚如雪隔箍。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,950評(píng)論 1 291
  • 那天脚乡,我揣著相機(jī)與錄音蜒滩,去河邊找鬼。 笑死奶稠,一個(gè)胖子當(dāng)著我的面吹牛俯艰,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播锌订,決...
    沈念sama閱讀 39,090評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼竹握,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了瀑志?” 一聲冷哼從身側(cè)響起涩搓,我...
    開封第一講書人閱讀 37,817評(píng)論 0 268
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎劈猪,沒想到半個(gè)月后昧甘,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,275評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡战得,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,592評(píng)論 2 327
  • 正文 我和宋清朗相戀三年充边,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片常侦。...
    茶點(diǎn)故事閱讀 38,724評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡浇冰,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出聋亡,到底是詐尸還是另有隱情肘习,我是刑警寧澤,帶...
    沈念sama閱讀 34,409評(píng)論 4 333
  • 正文 年R本政府宣布坡倔,位于F島的核電站漂佩,受9級(jí)特大地震影響脖含,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜投蝉,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,052評(píng)論 3 316
  • 文/蒙蒙 一养葵、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧瘩缆,春花似錦关拒、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,815評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至涌韩,卻和暖如春畔柔,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背臣樱。 一陣腳步聲響...
    開封第一講書人閱讀 32,043評(píng)論 1 266
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留腮考,地道東北人雇毫。 一個(gè)月前我還...
    沈念sama閱讀 46,503評(píng)論 2 361
  • 正文 我出身青樓,卻偏偏與公主長得像踩蔚,于是被迫代替她去往敵國和親棚放。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,627評(píng)論 2 350

推薦閱讀更多精彩內(nèi)容