pg.ErrNoRows和pg.ErrMultiRows
golang pg.v4的Select,返回结果有可能是0行 1行或者多行,如果根据返回的行数进行下一步的insert 或update,应该怎么做? 如果使用
b:= &[]Book{}
err:= db.Model(b).Where("blabla").Select()
除非是数据库连接异常才会抛出error,而
b:= &Book{}
err:= db.Model(b).Where("blabla").Select()
会在select没有结果时抛出pg.ErrNoRows
,有多行时抛出pg.ErrMultiRows
,1行时返回nil
,数据库异常时error
。
看下源码实现:
db.go
func (db *DB) Model(model interface{}) *orm.Query {
return orm.NewQuery(db, model)
}
> orm/query.go
func NewQuery(db dber, v …interface{}) *Query {
q := Query{
db: db,
}
switch l := len(v); {
case l == 1:
v0 := v[0]
if v0 != nil {
q.model, q.err = newTableModel(v0)
}
...
}
...
return &q
}
orm/model_table.go
func newTableModel(v interface{}) (tableModel, error) {
switch v := v.(type) {
...
case reflect.Value:
return newTableModelValue(v)
...
}
}
func newTableModelValue(v reflect.Value) (tableModel, error) {
...
switch v.Kind() {
case reflect.Struct:
return newStructTableModel(v)
case reflect.Slice:
...
if elType.Kind() == reflect.Struct {
return &sliceTableModel{
structTableModel: structTableModel{
table: Tables.Get(elType),
root: v,
},
slice: v,
}, nil
}
}
...
orm/query.go
func (q *Query) Select(values …interface{}) error {
...
if m, ok := model.(useQueryOne); ok && m.useQueryOne() {
_, err = q.db.QueryOne(model, sel, q.model)
} else {
_, err = q.db.Query(model, sel, q.model)
}
if err != nil {
return err
}
...
简单来说就是,当使用struct时,最终调用的时db的queryone() 而queryone函数中调用了assertone()函数对返回的结果行数进行了判断;而使用slice时,最终调用的时db的query()函数,query函数没有对返回的结果行数进行判断。
一点题外话,postgres9.5及以上版本提供selectorcreate /insertorupdate ,pg.v4 利用onconflict()实现,方便多了。