Beego脱坑(十四)ORM高级查询


title: Beego脱坑(十四)ORM高级查询
tags: go,beego,orm
author : Clown95


本文将讲述beego ORM的一些高级查询,这也是ORM最核心的部分,本文所有的操作都基于下面的student

image

Operators

在beego ORM中也有类似于SQL语句中的操作符,欲善其事,必利其器,我们先来了解他们,beego ORM目前支持的操作符为:

操作符 说明 等同Sql操作符
gt greater的缩写,表示大于的意思 >
gte greater than or equal的缩写,即大于等于 >=
lt less than的缩写,表示小于 <
lte less thanor equal的缩写,小于等于 <=
in 等同与sql语言中的in in
exact 等于 =
contains 包含,一般用于字符类型,如包含某某字符 like '%查询内容%'
startswith 以…起始,一般用于字符类型,如从什么字符开始 like '开始字符%'
endswith 以…结束 ,一般用于字符类型,如以什么字符结束 like '%结束字符'
isnull 表示改字段不能为空

==在beego中 操作符前面添加 字母 i 表示忽略大小写,如 iexact、icontains 、istartswith 和iendswith #E91E63==:

exact

我们最先演示下exact 它相当于 = ,现在我们需要通过它查询出 stu_id =3 的一条数据。

func (this *OperatorsController) GetExact() {
    orm := orm.NewOrm()
    // 获取表句柄
    stu := Student{}
    qs := orm.QueryTable("student")
    // select * from student where id =3;
    err := qs.Filter("stu_id__exact", 3).One(&stu) // 过滤器
    if err != nil {
        this.Ctx.WriteString(strconv.Itoa(stu.StuId) + " " + stu.Name + " " + stu.Age + " " + stu.Hobby)
    }else{
        this.Ctx.WriteString("查询失败")    
    }
}
image

上面的代码相当于sql语句 :

select * from student where id =3;
  • QueryTable("student") : 返回 student表的QuerySeter 对象。ORM 以 QuerySeter 来组织查询,每个返回 QuerySeter 的方法都会获得一个新的 QuerySeter 对象;
  • Filter("stu_id__exact" ,3) : 用来过滤查询结果,起到包含条件的作用,第一个参数是我匹配规则,第二个参数是我们匹配的值 ; 使用如果使用操作符,字段和操作符直接使用两个"_"连接,具体格式为:字段__操作符;
  • One(&stu) : 返回单条记录 ,参数一般是结构体对象。

gte

现在我们来使用 gte 获取 age>=25岁的所有数据, 下面我们看代码。

func (this *OperatorsController) GetAllGte() {
    orm := orm.NewOrm()
    var stus = []*Student{}
    // 获取表句柄
    qs := orm.QueryTable("student")
    // select * from student where age>=25;
    n, err := qs.Filter("age__gte", 20).All(&stus) // 过滤器
    if err == nil && n > 0 {
        for i := 0; i < len(stus); i++ {
            this.Ctx.WriteString(strconv.Itoa(stus[i].StuId) + " " + stus[i].Name + " " + stus[i].Age + " " + stus[i].Hobby + "\n")
        }
    } else {
        this.Ctx.WriteString("查询失败")
    }
}
image

上面的代码相当于sql语句:

select * from student where age>=25;
  • All(&stus) : 返回对应的结果集对象。 和One不一样的是,All返回所有符合条件的数据,参数是指针切片( []Type 或者[]*Type ),返回数据cols和error

in

现在我们通过 in 查询 stu_id 为 1、4、5、10的数据集。

func (this *OperatorsController) GetIn() {
    orm := orm.NewOrm()
    var stus = []*Student{}
    // 获取表句柄
    qs := orm.QueryTable("student")
    // select * from student where stu_id in(1,4,5,10);
    n, err := qs.Filter("stu_id__in",1,4,5,10 ).All(&stus) // 过滤器
    if err == nil && n > 0 {
        for i := 0; i < len(stus); i++ {
            this.Ctx.WriteString(strconv.Itoa(stus[i].StuId) + " " + stus[i].Name + " " + stus[i].Age + " " + stus[i].Hobby + "\n")
        }
    } else {
        this.Ctx.WriteString("查询失败")
    }
}
image

上面的代码相当于sql语句:

select * from student where stu_id in(1,4,5,10);

contains

接着我们使用 contains 来查询 name中包含 "小" 字的数据集。

func (this *OperatorsController) GetContains() {
    orm := orm.NewOrm()
    var stus = []*Student{}
    // 获取表句柄
    qs := orm.QueryTable("student")
    // select * from student where name like '小%';
    n, err := qs.Filter("name__contains", "小").All(&stus) // 过滤器
    if err == nil && n > 0 {
        for i := 0; i < len(stus); i++ {
            this.Ctx.WriteString(strconv.Itoa(stus[i].StuId) + " " + stus[i].Name + " " + stus[i].Age + " " + stus[i].Hobby + "\n")
        }
    } else {
        this.Ctx.WriteString("查询失败")
    }
}
image

上面的代码相当于sql语句:

select * from student where name like '小%';

操作符我们就演示到这里,其他的几个用法都大同小异,大家可以自己拓展。

高级查询接口

高级查询接口我们在 Operators 部分里面,使用过 Filter、One、All这三个了。我们就不在重复介绍了,我们主要来了解下其他的接口函数。

方法 说明
Exclude 用来过滤查询结果,起到 排除条件的作用,相当于!Filter
Limit 限制最大返回数据行数
Offset 设置偏移行数
GroupBy 数据分组
OrderBy 数据排序
Distinct 获取不重复的数据
Count 依据当前的查询条件,返回结果行数
Exist 判断查询的数据是否存在
Values 返回结果集数据,存储到map中
ValuesList 顾名思义,返回的结果集以slice存储
PrepareInsert 用于批量插入数据
Delete 依据当前查询条件,进行批量删除操作
Update 依据当前查询条件,进行批量更新操作

Exclude

现在我们来使用Exclude 排除 年龄小于等于25的数据.

func (this * ADvancedApiController) GetExclude(){
    orm := orm.NewOrm()
    var stus = []*Student{}
    // 获取表句柄
    qs := orm.QueryTable("student")
    //select * from student where not age <=25 ;
    n, err := qs.Exclude("age__lte",25).All(&stus)
    if err == nil && n > 0 {
        for i := 0; i < len(stus); i++ {
            this.Ctx.WriteString(strconv.Itoa(stus[i].StuId) + " " + stus[i].Name + " " + stus[i].Age + " " + stus[i].Hobby + "\n")
        }
    } else {
        this.Ctx.WriteString("查询失败")
    }
}

上面的代码相当于sql语句:

select * from student where not age <=25 ;
image

Limit

有时候数据过多,我们只想显示部分数据,比如现在我们只需要显示前5个数据

func (this * ADvancedApiController) GetLimit(){
    orm := orm.NewOrm()
    var stus = []*Student{}
    // 获取表句柄
    qs := orm.QueryTable("student")
    //select * from student limit 5 ;
    n, err := qs.Limit(5) .All(&stus)
    if err == nil && n > 0 {
        for i := 0; i < len(stus); i++ {
            this.Ctx.WriteString(strconv.Itoa(stus[i].StuId) + " " + stus[i].Name + " " + stus[i].Age + " " + stus[i].Hobby + "\n")
        }
    } else {
        this.Ctx.WriteString("查询失败")
    }
}
image

上面的代码相当于sql语句:

select * from student limit 5 ;

当然我们还可以设置偏移量来设置分页 ,我们来修改Limit,显示6-10的数据:

    n, err := qs.Limit(5,5) .All(&stus)
image

GroupBy

通过 age 进行分组

func (this * ADvancedApiController) GetGroupBy(){
    orm := orm.NewOrm()
    var stus = []*Student{}
    qs := orm.QueryTable("student")
    n, err := qs.GroupBy("age").All(&stus)
    if err == nil && n > 0 {
        for i := 0; i < len(stus); i++ {
            this.Ctx.WriteString(strconv.Itoa(stus[i].StuId) + " " + stus[i].Name + " " + stus[i].Age + " " + stus[i].Hobby + "\n")
        }
    } else {
        this.Ctx.WriteString("查询失败")
    }
}

OrderBy

通过 age 和 stu_id 进行排序 表达式前面使用 "-" 代表降序排序

func (this * ADvancedApiController) GetOrderBy(){
    orm := orm.NewOrm()
    var stus = []*Student{}
    qs := orm.QueryTable("student")
    n, err := qs.OrderBy("-age","stu_id").All(&stus)
    if err == nil && n > 0 {
        for i := 0; i < len(stus); i++ {
            this.Ctx.WriteString(strconv.Itoa(stus[i].StuId) + " " + stus[i].Name + " " + stus[i].Age + " " + stus[i].Hobby + "\n")
        }
    } else {
        this.Ctx.WriteString("查询失败")
    }
}

Count

使用Count 查询有多少行数据

func (this *ADvancedApiController) GetCount() {
    orm := orm.NewOrm()
    //select count(*) from student;
    n, err := orm.QueryTable("student").Count()
    if err != nil {
        this.Ctx.WriteString("查询出错!\n")
        return
    }else {
        this.Ctx.WriteString("n = " + strconv.Itoa(int(n)))
    }
}
image

上面的代码相当于sql语句:

select count(*) from student;

Exist

我们用 Exist 判断 stu_id =8 和 stu_id =15 的数据是否存在

func (this *ADvancedApiController) GetExist() {
    orm := orm.NewOrm()
    qs := orm.QueryTable("student")
    flag := qs.Filter("stu_id", 8).Exist()
    if flag {
        this.Ctx.WriteString("stu_id=8 存在\n")
    }else {
        this.Ctx.WriteString("stu_id=8 不存在\n")
    }
    flag = qs.Filter("stu_id", 15).Exist()
    if flag {
        this.Ctx.WriteString("stu_id=15 存在\n")
    }else {
        this.Ctx.WriteString("stu_id=15 不存在\n")
    }
}
image

Values

我们还可以 使用Values 返回结果集的 key => value 值 ,key 为Model里的Field name, value的值是interface{}类型,如果你要将value赋值给struct中的某字段,需要根据结构体对应字段类型使用断言获取真实值。

func (this *ADvancedApiController) GetValues() {
    //将每一条记录的字段作为键,数据作为值存入map中,每个map就是一条记录。
    var maps []orm.Params //[map, map, map]
    orm := orm.NewOrm()
    _, err := orm.QueryTable("student").Values(&maps, )
    if err != nil {
        this.Ctx.WriteString("查询出错!")
        return
    }
    for _, m := range maps {
        this.Ctx.WriteString(m["Name"].(string) + " " + m["Age"].(string) + "   " + m["Hobby"].(string) + "\n")
    }
}
enter description here

当然我们还可以直接指定 expr 级联返回需要的数据

func (this *ADvancedApiController) GetValuesField() {
    //将每一条记录的字段作为键,数据作为值存入map中,每个map就是一条记录。
    var maps []orm.Params //[map, map, map]
    orm := orm.NewOrm()
    _, err := orm.QueryTable("student").Values(&maps, "Name","Hobby")
    if err != nil {
        this.Ctx.WriteString("查询出错!")
        return
    }
    for _, m := range maps {
        this.Ctx.WriteString(m["Name"].(string) + " " + m["Hobby"].(string) + "\n")
    }
}

ValuesList

顾名思义,返回的结果集以slice存储,结果的排列与 Model 中定义的 Field 顺序一致,返回的每个元素值以 string 保存。

func (this *ADvancedApiController) GetValuesList() {
    var list []orm.ParamsList
    orm := orm.NewOrm()
    _, err := orm.QueryTable("student").ValuesList(&list, "Name", "Age", "Hobby")
    if err != nil {
        this.Ctx.WriteString("查询出错!")
        return
    }
    for _, row := range list {
        for _, col := range row {
            this.Ctx.WriteString(col.(string) + " ")
        }
        this.Ctx.WriteString("\n")
    }
}
enter description here

PrepareInsert

我们使用PrepareInsert 在添加点数据,并且我们多提交一次数据,方便演示下面的批量删除

func (this *ADvancedApiController) GetPrepareInsert() {

    var stus []*Student
    stu1 := Student{Name:"银之介", Age:"60", Hobby:"辣妹"}
    stu2 := Student{Name:"正男", Age:"5", Hobby:"小爱"}
    stu3 := Student{Name:"阿呆", Age:"6", Hobby:"甩鼻涕"}
    stu4 := Student{Name:"园长", Age:"56", Hobby:"园艺"}
    stus = append(stus, &stu1, &stu2, &stu3, &stu4)
    orm := orm.NewOrm()
// insert into student(name, age.hobby) values("银之介", "60", "辣妹"), (正男", "5", "小爱");
    insert, _ := orm.QueryTable("student").PrepareInsert()
    for _, stu := range stus {
        _, err := insert.Insert(stu)
        if err != nil {
            fmt.Println("插入错误!\n")
            continue
        }
    }
    insert.Close()
    this.Ctx.WriteString("插入结束!\n")
}

上面的代码相当于sql语句:

insert into student(name, age.hobby) values("银之介", "60", "辣妹"), ....,(园长", "56", "园艺");

Delete

还记得我们刚刚多提交了一次数据插入吗?我们原来的数据stu_id 最大为11, 所以我们删除 stu_id >11 的数据

func (this *ADvancedApiController) GetDelete() {
    orm := orm.NewOrm()
    qs:= orm.QueryTable("student")
    n,err:=qs.Filter("stu_id__gt",11).Delete()
    if err == nil&& n>0 {
        this.Ctx.WriteString("删除成功!\n")

    } else {
        this.Ctx.WriteString("删除失败!\n")
    }
}

Update

现在我们来批量更新下数据,下面我们需要把 stu_id=5 的 Hobby 改为 学习。

func (this *ADvancedApiController) GetUpdate() {
    orm1 := orm.NewOrm()
    qs:= orm1.QueryTable("student")
    n,err:=qs.Filter("stu_id",5).Update(orm.Params{
            "hobby": "学习",
        })
    if err == nil&& n>0 {
        this.Ctx.WriteString("修改成功!\n")

    } else {
        this.Ctx.WriteString("修改失败!\n")
    }
}

看到这里大家应该基本掌握接口的用法了,还有其他部分没演示的,请自行拓展我实在不想写了。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,911评论 5 460
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 82,014评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 142,129评论 0 320
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,283评论 1 264
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,159评论 4 357
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,161评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,565评论 3 382
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,251评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,531评论 1 292
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,619评论 2 310
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,383评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,255评论 3 313
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,624评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,916评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,199评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,553评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,756评论 2 335

推荐阅读更多精彩内容