内容简介:在介绍之前先说明一下,标题中带有【beego】标签的,是beego框架使用中遇到的坑。如果没有,那就是golang本身的坑。当然,此坑并非人家代码有问题,有几个地方反而是出于性能等各方面的考量有意而为之。但这些地方却是一门语言或框架的初学者大概率会遇到的困惑。传送门:java程序员对于持久层框架可能都用过mybatis,对查询操作也会比较熟悉。当一行代码下去,数据库给你咔咔一顿找,发现没有结果时,会返回null 或 一个空的collection。beego中也有持久层框架,就叫orm。对于查询方面的处理,
在介绍之前先说明一下,标题中带有【beego】标签的,是beego框架使用中遇到的坑。如果没有,那就是golang本身的坑。当然,此坑并非人家代码有问题,有几个地方反而是出于性能等各方面的考量有意而为之。但这些地方却是一门语言或框架的初学者大概率会遇到的困惑。
传送门: 细数在用golang&beego做api server过程中的坑(一)
【beego】4、orm查询时没有对应结果集: now row found (error)
java程序员对于持久层框架可能都用过mybatis,对查询操作也会比较熟悉。当一行代码下去,数据库给你咔咔一顿找,发现没有结果时,会返回null 或 一个空的collection。beego中也有持久层框架,就叫orm。对于查询方面的处理,如果数据库查询后没有找到任何结果,orm不会返回空,而是会返回一个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 }
这段代码是我从现有工程里面随便摘出来的,大意就是通过用户id筛选出积分表里的一条记录。这个时候如果查不到对应的记录, err != nil
便会成立。这时候单纯的通过 err !=nil
就无法判断到底是真的出现数据库查询错误,还是没有找到对应记录。因此就有了 models.IsNoRowFoundError(err)
这一判断。 对于是否是 NoRowFoundError
,可行的方法之一是判断err是否相等:
if err == orm.ErrNoRows { }
之二是判断error message:
//判断一个错误是不是因为数据库中没有符合条件的记录,而抛出的错误。 //这种情况是一种正常的情况,不应该作为错误抛出 func IsNoRowFoundError(err error) bool { if err == nil { return false } if strings.Contains(err.Error(), "no row found") { return true } return false }
当然对于其他的查询方式,也会有这个问题,例如:
o := orm.NewOrm() err = o.Read(&orderInfo, "OuterOrderId")
在此就不一一列举。
我个人认为astaxie大神在设计beego orm时不可能没考虑到这一点。之所以这样设计,可能的一个原因是,对于刚才的查询代码:
var container po.OnlineAccumulateRecord err = o.QueryTable(new(po.OnlineAccumulateRecord)).Filter("User", user.Id).One(&container)
不能够通过判断container是不是 nil
来断定有没有查询到对应的记录。因为在golang中,一个变量由两部分组成: type & value
,只要任何一部分是明确的,则该变量就不是nil。在上例中,container的 type
已经明确声明是 po.OnlineAccumulateRecord
, 此时value是零值。因此它永远都不会是nil。
【beego】5、orm插入记录,如果pk是非整型(e.g. string):no LastInsertId available error
orm设计时对于整型pk做了特别的支持:
当 Field 类型为 int, int32, int64, uint, uint32, uint64 时,可以设置字段为自增健
当模型定义里没有主键时,符合上述类型且名称为 Id 的 Field 将被视为自增健。
但如果主键是string,通过以下代码做insert操作时会返回"no LastInsertId available" error。因此需要增加 IsNoLastInsertIdError 判断以免影响程序运行的分支走向。
o := orm.NewOrm() _, err = o.Insert(wechatNotify) if models.IsNoLastInsertIdError(err) { return nil }
同样的, IsNoLastInsertIdError 判断的方法之一是根据error message判断:
//这种情况是一种正常的情况,不应该作为错误抛出 func IsNoLastInsertIdError(err error) bool { if err == nil { return false } if strings.Contains(err.Error(), "no LastInsertId available") { return true } return false }
【beego】6、报错误导:<Ormer> table: '.' not found,
好不容易把代码撸完了,兴冲冲的运行debug,结果发现如下报错信息:
<Ormer> table: `.` not found, make sure it was registered with `RegisterModel()`
根据字面意思是说正在操作的这个名字为“.”的table没有进行regist操作。于是排查,但很快发现:
1、根本就没有一个叫"."的table
2、正在操作的表也已经在beego启动的时候完成了regist操作
然后通过源码进行定位,报错位置:
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)) }
那这个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 返回值。那什么时候他们返回的是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
从上述代码可以看出,当 o.Insert()
接收到参数是 not defined
的话,就会导致该项错误,而并不是说这个玩意没有regist(就这些玩意咋去regist嘛)。 注释部分也说了not defined包括哪几种: *T, struct{}, []int
,以及这几类的别名。
最后举个能出现这个问题的例子:
var _struct structExample var _pointer &_struct o := orm.NewOrm() o.Insert(&_pointer)
【beego】7、报错误导:panic: reflect: call of reflect.Value.Interface on zero Value
再一次好不容易把代码撸完了,兴冲冲的运行debug,结果发现如下报错信息:
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
可以通过堆栈信息看出这一报错发生在regist model时,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 }
好像代码写的没毛病。只好再根据堆栈信息找到beego源码中: github.com/astaxie/beego/orm.getFieldType 位于 astaxie/beego/orm/models_utils.go:181
, getFieldType
方法中是一堆类型判断代码:
// 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 ··· ···
什么时候会走到这?那就看看 github.com/astaxie/beego/orm.newFieldInfo 位于 gthub.com/astaxie/beego/orm/models_info_f.go:244
的代码:
这些代码都是一些对 字段tag 的分析处理。等等,是不是忘记添加tag了!!
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,搞定!!
User *User `orm:"rel(fk)"`
那是不是所有的字段在orm时都需要添加标签? 不是。当字段是指针类型时,如果没有用 orm:"-"
进行orm忽略,必须要添加标签来进行表关系设置。
相关资料: https://beego.me/docs/mvc/model/models.md 参照其中的表关系设置章节。
以上所述就是小编给大家介绍的《细数在用golang&beego做api server过程中的坑(二)》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- 存储过程 – 重新编译后,存储过程运行得很快
- 面试:谈谈你对 MyBatis 执行过程之 SQL 执行过程理解
- 死磕Android_App 启动过程(含 Activity 启动过程)
- 【PHP源码学习】关于$a=1整个过程的gdb过程与相关验证
- Spring的Property配置加载和使用过程及Environment的初始化过程
- [译]从输入URL到页面呈现的超详细过程——第二步:Tags转化成DOM的过程
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Automate This
Christopher Steiner / Portfolio / 2013-8-9 / USD 25.95
"The rousing story of the last gasp of human agency and how today's best and brightest minds are endeavoring to put an end to it." It used to be that to diagnose an illness, interpret legal docume......一起来看看 《Automate This》 这本书的介绍吧!