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)

操作符

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 接口和其部分方法的使用。限于篇幅,没有介绍关联查询和构造查询,关于未提及的内容,读者朋友可以参考官方手册。

参考资料:

https://beego.me/docs