内容简介:在介绍之前先说明一下,标题中带有【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的过程
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
SHA 加密
SHA 加密工具
Markdown 在线编辑器
Markdown 在线编辑器