18

Golang 语言 Web 框架 beego v2 之读操作

 3 years ago
source link: https://mp.weixin.qq.com/s?__biz=MzA4Mjc1NTMyOQ%3D%3D&%3Bmid=2247484201&%3Bidx=1&%3Bsn=80e24b9eaefe3e1f501c6910de7d3611
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

01

介绍

beego ORM 是一个强大的 Go 语言 ORM 框架。她的灵感主要来自

Django ORM 和 SQLAlchemy。

已支持的数据库驱动有MySQL、PostgreSQL 和 Sqlite3。

beego v2.x 和 beego v1.x 在 ORM 上的区别是,beego v2.x 的 ORM 对象被设计为无状态的,它是线程安全的,建议大家在使用时,一个数据库只对应一个 ORM 对象。

本文全篇都是以 MySQL 为例。

关于 beego ORM 的安装和注册,已在「 Golang 语言 Web 框架 beego v2 之写操作 」中介绍,本文不再赘述。

02

普通查询

beego ORM 提供了两个普通查询的方法,分别是 Read 和 ReadOrCreate。

Read 方法默认把主键作为查询条件,也可以指定字段作为查询条件,如果指定字段作为查询条件,需要在 Read 方法的第二个参数中传入指定字段的名称。

示例代码:

Read 方法,主键查询

func (u *UserController) Read() {
o := orm.NewOrm()
user := &models.User{
Id: 2,
}
err := o.Read(user)
if err != nil {
log.Fatalln(err.Error())
return
}
fmt.Printf("user:%+v\n", user)
}

Read 方法,指定字段查询

func (u *UserController) Read() {
o := orm.NewOrm()
user := &models.User{
Name: "Lucy",
}
err := o.Read(user, "Name")
if err != nil {
log.Fatalln(err.Error())
return
}
fmt.Printf("user:%+v\n", user)
}

ReadOrCreate 方法默认必须传入一个参数作为条件字段,同时支持多个参数作为条件字段。根据条件字段从数据库中读取行,如果不存在,就插入一行。

ReadOrCreate 方法返回三个值,分别为一个 bool 类型,代表是否新插入一行;一个 int64 类型,代表查询对象(或新插入)的 Id;和一个 error 类型的错误。

示例代码:

ReadOrCreate 方法

func (u *UserController) Read() {
o := orm.NewOrm()
user := &models.User{
Name: "Alan",
Age: 37,
}
created, id, err := o.ReadOrCreate(user,"Name", "Age")
if err != nil {
log.Fatalln(err.Error())
return
}
fmt.Printf("Created:%t,id:%d\n", created, id)
fmt.Printf("user:%+v\n", user)
}

03

高级查询

beego ORM 高级查询是通过获取一个 QuerySeter 对象,使用 QuerySeter 对象的方法实现高级查询。

Query Seter 接口包含的方法:

type QuerySeter interface {
Filter(string, ...interface{}) QuerySeter
FilterRaw(string, string) QuerySeter
Exclude(string, ...interface{}) QuerySeter
SetCond(*Condition) QuerySeter
GetCond() *Condition
Limit(limit interface{}, args ...interface{}) QuerySeter
Offset(offset interface{}) QuerySeter
GroupBy(exprs ...string) QuerySeter
OrderBy(exprs ...string) QuerySeter
ForceIndex(indexes ...string) QuerySeter
UseIndex(indexes ...string) QuerySeter
IgnoreIndex(indexes ...string) QuerySeter
RelatedSel(params ...interface{}) QuerySeter
Distinct() QuerySeter
ForUpdate() QuerySeter
Count() (int64, error)
Exist() bool
Update(values Params) (int64, error)
Delete() (int64, error)
PrepareInsert() (Inserter, error)
All(container interface{}, cols ...string) (int64, error)
One(container interface{}, cols ...string) error
Values(results *[]Params, exprs ...string) (int64, error)
ValuesList(results *[]ParamsList, exprs ...string) (int64, error)
ValuesFlat(result *ParamsList, expr string) (int64, error)
RowsToMap(result *Params, keyCol string, valueCol string) (int64, error)
RowsToStruct(ptrStruct interface{}, keyCol string, valueCol string) (int64, error)
}

QuerySeter 对象

在介绍 QuerySeter 对象的方法之前,先给大家介绍如何获取一个  QuerySeter 对象,获取一个  QuerySeter 对象有三种方式,第一种是调用 ormer 的QueryTable 方法,参数传入一个 string 类型的表名; 第二种是 调用 ormer  的 QueryTable  方法, 参数传入一个结构体的地址 第三种是 调用 ormer  的 QueryTable  方法, 参数传入一个指针类型的结构体

示例代码:

var user models.User
err := o.QueryTable("beego_user").One(&user)
err = o.QueryTable(&user).One(&user)
err = o.QueryTable(new(models.User)).One(&user)

本小节我们主要介绍一下 One 方法、All 方法和 Count 方法。

One 方法

One 方法返回单条记录,默认情况下,返回主键升序的第一条记录。如果指定查询条件,则返回符合查询条件的一条记录,如果符合查询条件的记录大于 一条,则返回错误。

One 方法默认返回记录的所有字段,如果需要指定返回的字段,可以在 One 方法中传入需要返回的字段名称,多个字段名称以英文逗号分隔,未指定的返回字段,返回该字段的类型零值。

示例代码:

func (u *UserController) Read() {
o := orm.NewOrm()
var user models.User
// string 类型的表名
// err := o.QueryTable("beego_user").One(&user)
// 结构体的地址
// err := o.QueryTable(&user).One(&user)
// 使用对象作为表名
err := o.QueryTable(new(models.User)).One(&user)
// 指定返回字段,其他字段返回字段类型的零值
// err := o.QueryTable(new(models.User)).One(&user, "Id", "Name")
if err != nil {
log.Fatalln(err.Error())
return
}
fmt.Printf("user:%+v\n", user)
}

All 方法

All 方法返回对应的结果集对象,默认受 Limit 限制,最多显示 1000 条数据。All 方法的参数可以接收 []Type 和 *[]Type 两种形式的切片,如果需要指定查询的字段,可以在第二个参数开始传入字段名称,多个字段名称以英文逗号分隔,未指定查询的字段,返回字段类型的零值。

示例代码:

func (u *UserController) Read() {
o := orm.NewOrm()
var users []models.User
rows, err := o.QueryTable("beego_user").All(&users)
// 指定返回字段,其他字段返回字段类型的零值
// rows, err := o.QueryTable("beego_user").All(&users, "Id", "Name")
if err != nil {
log.Fatalln(err.Error())
return
}
fmt.Printf("rows:%d users:%+v\n", rows, users)
}

Count 方法

Count 方法返回结果集行数。

示例代码:

func (u *UserController) Read() {
o := orm.NewOrm()
num, err := o.QueryTable(new(models.User)).Count()
if err != nil {
log.Fatalln(err.Error())
return
}
fmt.Println("num:", num)
}

04

条件查询

上一小节介绍的查询方式,都没有使用查询条件,本小节内容介绍条件查询,在介绍条件查询之前,先来介绍一下 expr,expr 是 QuerySeter 用于描述字段和描述sql 操作符的一种表达方式。

字段组合的前后顺序依照表的关系,比如 User 表拥有 Profile 的外键,那么对 User 表查询对应的 Profile.Age 为条件,则使用 Profile__Age。

注意,字段的分隔符号使用双下划线 __,除了描述字段, expr 的尾部可以增加操作符以执行对应的 sql 操作。比如 Profile__Age__gt 代表 Profile.Age > 18 的条件查询。

expr 示例代码:

qs.Filter("id", 1) // WHERE id = 1
qs.Filter("profile__age", 18) // WHERE profile.age = 18
qs.Filter("Profile__Age", 18) // 使用字段名和 Field 名都是允许的
qs.Filter("profile__age__gt", 18) // WHERE profile.age > 18
qs.Filter("profile__age__gte", 18) // WHERE profile.age >= 18
qs.Filter("profile__age__in", 18, 20) // WHERE profile.age IN (18, 20)


qs.Filter("profile__age__in", 18, 20).Exclude("profile__lt", 1000)
// WHERE profile.age IN (18, 20) AND NOT profile_id < 1000

注释后面将描述对应的 sql 语句,仅仅是描述 expr 的类似结果,并不代表实际生成的语句。

表达式和操作符

在介绍 QuerySeter 的方法之前,先介绍 表达式和操作符,表达式和操作符适用于  QuerySeter 的所有方法。

表达式

  • 等于

  • 大于 gt

  • 大于等于 gte

  • 小于 lt

  • 小于等于 lte

  • IN

  • isnull (true:isnull / false:is not null)

操作符

  • exact 等于(区分字母大小写)

  • iexact 等于(不区分大小写)
  • contains Like(区分大小写)
  • icontains Like( 不区分大小写)
  • startswith (前置模糊查询,区分大小写)
  • istartswith(前置模糊查询,不区分大小写)
  • endswith(后置模糊查询,区分大小写)
  • iendswith(后置模糊查询,不区分大小写)

QuerySeter 的方法

Filter 包含

Filter 方法用来过滤查询结果,起到「包含条件」的作用。

Exclude 排除

Exclude 方法用来过滤查询结果,起到「排除条件」的作用。

<strong>Limit 限制条数</strong>

Limit 方法限制最大返回的记录数,默认值为 1000。第二个参数可以设置 offset,需要特别注意的是,这里的 limit / offset 和原生 sql 中的 limit / offset 是反过来的。

<strong>Offset 偏移</strong>

Offset 方法用来设置偏移量。

<strong>OrderBy 排序 "column" means ASC, "-column" means DESC.</strong>

OrderBy 方法用于排序,参数使用 expr 表达方式,默认是 ASC 排序规则,在 expr 前面用减号「-」表示 DESC 排序规则。

Distinct 方法

Distinct 方法返回指定字段不重复的查询结果。

<strong>Exist 是否存在</strong>

Exist 方法用于判断符合查询条件的结果是否存在。

示例代码:

func (u *UserController) Read() {
o := orm.NewOrm()
// 条件查询
var users []models.User
// Filter 包含
// 表达式和操作符
// 等于
err := o.QueryTable(new(models.User)).Filter("id", 2).One(&users)
// 大于
// num, err := o.QueryTable(new(models.User)).Filter("id__gt", 9).All(&users)
// 大于等于
// num, err := o.QueryTable(new(models.User)).Filter("id__gte", 9).All(&users)
// 小于
// num, err := o.QueryTable(new(models.User)).Filter("id__lt", 5).All(&users)
// 小于等于
// num, err := o.QueryTable(new(models.User)).Filter("id__lte", 5).All(&users)
// IN
// num, err := o.QueryTable(new(models.User)).Filter("id__in", 2, 4).All(&users)
// isnull (true:isnull / false: is not null)
// num, err := o.QueryTable(new(models.User)).Filter("id__isnull", false).All(&users)
// num, err := o.QueryTable(new(models.User)).Filter("id__isnull", true).All(&users)


// exact 等于(区分字母大小写)
// num, err := o.QueryTable(new(models.User)).Filter("name__exact", "frank").All(&users)
// iexact 等于(不区分大小写)
// num, err := o.QueryTable(new(models.User)).Filter("name__iexact", "frank").All(&users)


// contains Like(区分大小写)
// num, err := o.QueryTable(new(models.User)).Filter("name__contains", "frank").All(&users)
// icontains Like( 不区分大小写)
// num, err := o.QueryTable(new(models.User)).Filter("name__icontains", "frank").All(&users)


// startswith (前置模糊查询,区分大小写)
// num, err := o.QueryTable(new(models.User)).Filter("name__startswith", "fran").All(&users)
// istartswith(前置模糊查询,不区分大小写)
// num, err := o.QueryTable(new(models.User)).Filter("name__istartswith", "fran").All(&users)


// endswith(后置模糊查询,区分大小写)
// num, err := o.QueryTable(new(models.User)).Filter("name__endswith", "er").All(&users)
// iendswith(后置模糊查询,不区分大小写)
// num, err := o.QueryTable(new(models.User)).Filter("name__iendswith", "er").All(&users)


// Exclude 排除
// num, err := o.QueryTable(new(models.User)).Exclude("name__exact", "frank").All(&users)


// Limit 限制条数
// num, err := o.QueryTable(new(models.User)).Limit(4).All(&users)


// Offset 偏移
// num, err := o.QueryTable(new(models.User)).Offset(4).All(&users)


// OrderBy 排序 "column" means ASC, "-column" means DESC.
// num, err := o.QueryTable(new(models.User)).OrderBy("id").All(&users)
// num, err := o.QueryTable(new(models.User)).OrderBy("-id").All(&users)


// Distinct 去重
// num, err := o.QueryTable(new(models.User)).Filter("id__gt", 9).Distinct().All(&users, "Age")


// Exist 是否存在
// isExisted := o.QueryTable(new(models.User)).Filter("name__exact", "frank1").Exist()
// fmt.Println("isExisted:", isExisted)


if err != nil {
log.Fatalln(err.Error())
return
}
fmt.Printf("user:%+v\n", users)
}

05

原生 SQL 查询

beego ORM 原生 SQL 查询,通过获取一个 RawSeter 对象,使用 RawSeter 对象的 Raw 方法,实现原生 SQL 查询。

Raw 方法,参数 1 是原生 sql 语句的字符串,参数 2 是原生 sql 语句的参数,该参数支持模型结构体,切片和数组。

RawSeter 接口的方法:

type RawSeter interface {
Exec() (sql.Result, error)
QueryRow(containers ...interface{}) error
QueryRows(containers ...interface{}) (int64, error)
SetArgs(...interface{}) RawSeter
Values(container *[]Params, cols ...string) (int64, error)
ValuesList(container *[]ParamsList, cols ...string) (int64, error)
ValuesFlat(container *ParamsList, cols ...string) (int64, error)
RowsToMap(result *Params, keyCol string, valueCol string) (int64, error)
RowsToStruct(ptrStruct interface{}, keyCol string, valueCol string) (int64, error)
Prepare() (RawPreparer, error)
}

接下来,我们来介绍一下 QueryRow 方法和 QueryRows 方法。

QueryRow 方法

QueryRow 方法返回单条查询数据,不定长参数接收指针类型。

示例代码:

func (u *UserController) Read() {
o := orm.NewOrm()
var user models.User
err := o.Raw("SELECT id,name,age FROM beego_user WHERE id = ?", 2).QueryRow(&user)
if err != nil {
log.Fatalln(err.Error())
return
}
fmt.Printf("user:%+v\n", user)
}

QueryRows 方法

QueryRows 方法返回多条查询数据,不定长参数接收指针类型。返回结果是查询结果集的数量和错误。

示例代码:

func (u *UserController) Read() {
o := orm.NewOrm()
var users []models.User
ids := []int{1,3,5}
num, err := o.Raw("SELECT id,name,age FROM beego_user WHERE id IN (?,?,?)", ids).QueryRows(&users)
if err != nil {
log.Fatalln(err.Error())
return
}
fmt.Printf("nums:%d user:%+v\n", num, users)
}

06

总结

本文主要介绍 beego ORM 的读操作,包含普通查询、高级查询和原生 SQL 查询,先是介绍了普通查询,然后是介绍高级查询,包含 expr 表达式,QuerySeter 接口和其部分方法的使用,最后介绍了 RawSeter 接口和其部分方法的使用。限于篇幅,没有介绍关联查询和构造查询,关于未提及的内容,读者朋友可以参考官方手册。

B3yUfqm.jpg!mobile

参考资料:

https://beego.me/docs


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK