Beego 使用教程 10:ORM 操作数据库(下)

beego 是一个用于Go编程语言的开源、高性能的 web 框架

beego 被用于在Go语言中企业应用程序的快速开发,包括RESTful API、web应用程序和后端服务。它的灵感来源于Tornado, Sinatra 和 Flask

beego 官网:http://beego.gocn.vip/

上面的 beego 官网如果访问不到,看这篇文章《beego 官网文档本地环境搭建》

注意:本文的 beego 文档使用的就是本地环境搭建的文档,因为官网文档已经不可用了

beego 官方 github 仓库:https://github.com/beego/beego

上一讲,讲了 beego ORM 操作数据库,需要的朋友可以查看《Beego 使用教程 9:ORM 操作数据库(上)》

这一讲,继续讲解 ORM 操作数据库。代码使用上一讲的代码

目录

1、Orm 增删改查

1.1、添加

1.2、批量添加

1.3、删除

1.4、修改

1.5、查询

1.5.1、主键查询

1.5.2、非主键查询

2、事务

2.1、手动管理事务

2.2、闭包的方式管理事务

3、QueryBuilder 构造复杂查询

4、QuerySeter 复杂查询

5、原生查询


1、Orm 增删改查

如下就可以创建一个简单的Orm实例

var o orm.Ormer
o = orm.NewOrm() // 创建一个 Ormer
// NewOrm 的同时会执行 orm.BootStrap (整个 app 只执行一次),用以验证模型之间的定义并缓存。

大多数情况下,你应该尽量复用Orm 实例,因为本身Orm实例被设计为无状态的,一个数据库对应一个Orm实例

但是在使用事务的时候,我们会返回TxOrm的实例,它本身是有状态的,一个事务对应一个TxOrm实例。在使用TxOrm时候,任何衍生查询都是在该事务内

1.1、添加

定义

Insert(md interface{}) (int64, error)
InsertWithCtx(ctx context.Context, md interface{}) (int64, error)

代码看下面,是上一讲的代码

package dao

import (
	"github.com/beego/beego/v2/client/orm"
	//匿名引入mysql驱动
	_ "github.com/go-sql-driver/mysql"
)

type User struct {
	Id   int    `orm:"auto"`
	Name string `orm:"column(name)"`
	Age  int    `orm:"column(age)"`
}

func init() {
	// 注册 User 结构体模型
	orm.RegisterModel(new(User))

	orm.RegisterDataBase("default", "mysql", "root:123456@tcp(192.168.3.232:3306)/beego-demo-db?charset=utf8")

	orm.SetMaxIdleConns("default", 30)
}

func Add() {
	o := orm.NewOrm()

	user := new(User)
	user.Name = "贾元春"
	user.Age = 25

	// 添加数据
	o.Insert(user)
}

1.2、批量添加

InsertMulti(bulk int, mds interface{}) (int64, error)
InsertMultiWithCtx(ctx context.Context, bulk int, mds interface{}) (int64, error)

将 user.go 修改为下面代码

package dao

import (
	"github.com/beego/beego/v2/client/orm"
	//匿名引入mysql驱动
	_ "github.com/go-sql-driver/mysql"
)

type User struct {
	Id   int    `orm:"column(id)"`
	Name string `orm:"column(name)"`
	Age  int    `orm:"column(age)"`
}

var o orm.Ormer

func init() {
	// 注册 User 结构体模型
	orm.RegisterModel(new(User))

	orm.RegisterDataBase("default", "mysql", "root:123456@tcp(192.168.3.232:3306)/beego-demo-db?charset=utf8")

	orm.SetMaxIdleConns("default", 30)

	o = orm.NewOrm()
}

func Add() {
	user := new(User)
	user.Name = "贾元春"
	user.Age = 25

	// 添加数据
	o.Insert(user)
}

func AddMulti() {
	users := [2]User{{Name: "贾迎春", Age: 18}, {Name: "贾惜春", Age: 19}}

	o.InsertMulti(2, users)
}

修改 controller 目录下 dao.go 为下面代码

package controller

import (
	"beego-demo/dao"
	"github.com/beego/beego/v2/server/web"
	"github.com/beego/beego/v2/server/web/context"
)

func RegisterDaoRoutes() {
	web.Get("/user/add", func(ctx *context.Context) {
		dao.Add()
		ctx.WriteString("添加成功")
	})

	web.Get("/user/adds", func(ctx *context.Context) {
		dao.AddMulti()
		ctx.WriteString("操作成功")
	})
}

运行效果

浏览器访问:http://localhost:9090/user/adds

1.3、删除

使用主键来删除数据,定义

Delete(md interface{}, cols ...string) (int64, error)
DeleteWithCtx(ctx context.Context, md interface{}, cols ...string) (int64, error)

第一个返回值是受影响的行数

将 dao 目录下的 user.go 修改为下面代码

package dao

import (
	"fmt"
	"github.com/beego/beego/v2/client/orm"
	//匿名引入mysql驱动
	_ "github.com/go-sql-driver/mysql"
)

type User struct {
	Id   int    `orm:"column(id)"`
	Name string `orm:"column(name)"`
	Age  int    `orm:"column(age)"`
}

var o orm.Ormer

func init() {
	// 注册 User 结构体模型
	orm.RegisterModel(new(User))

	orm.RegisterDataBase("default", "mysql", "root:123456@tcp(192.168.3.232:3306)/beego-demo-db?charset=utf8")

	orm.SetMaxIdleConns("default", 30)

	o = orm.NewOrm()
}

func Add() {
	user := new(User)
	user.Name = "贾元春"
	user.Age = 25

	// 添加数据
	o.Insert(user)
}

func AddMulti() {
	users := [2]User{{Name: "贾迎春", Age: 18}, {Name: "贾惜春", Age: 19}}

	o.InsertMulti(2, users)
}

func Delete() {
	var user = User{Id: 7}
	c, er := o.Delete(&user)
	if er != nil {
		fmt.Println(er)
	}
	fmt.Println(c)
}

修改 controller 目录下 dao.go 为下面代码

package controller

import (
	"beego-demo/dao"
	"github.com/beego/beego/v2/server/web"
	"github.com/beego/beego/v2/server/web/context"
)

func RegisterDaoRoutes() {
	web.Get("/user/add", func(ctx *context.Context) {
		dao.Add()
		ctx.WriteString("添加成功")
	})

	web.Get("/user/adds", func(ctx *context.Context) {
		dao.AddMulti()
		ctx.WriteString("操作成功")
	})

	web.Get("/user/del", func(ctx *context.Context) {
		dao.Delete()
		ctx.WriteString("删除成功")
	})
}

运行效果

浏览器访问:http://localhost:9090/user/del

1.4、修改

使用主键来更新数据。也就是如果你使用这个方法,Beego 会尝试读取里面的主键值,而后将主键作为更新的条件

定义是

Update(md interface{}, cols ...string) (int64, error)
UpdateWithCtx(ctx context.Context, md interface{}, cols ...string) (int64, error)

如果你没有指定 cols 参数,那么所有的列都会被更新

第一个返回值是受影响的行数

将 dao 目录下的 user.go 修改为下面代码

package dao

import (
	"fmt"
	"github.com/beego/beego/v2/client/orm"
	//匿名引入mysql驱动
	_ "github.com/go-sql-driver/mysql"
)

type User struct {
	Id   int    `orm:"column(id)"`
	Name string `orm:"column(name)"`
	Age  int    `orm:"column(age)"`
}

var o orm.Ormer

func init() {
	// 注册 User 结构体模型
	orm.RegisterModel(new(User))

	orm.RegisterDataBase("default", "mysql", "root:123456@tcp(192.168.3.232:3306)/beego-demo-db?charset=utf8")

	orm.SetMaxIdleConns("default", 30)

	o = orm.NewOrm()
}

func Add() {
	user := new(User)
	user.Name = "贾元春"
	user.Age = 25

	// 添加数据
	o.Insert(user)
}

func AddMulti() {
	users := [2]User{{Name: "贾迎春", Age: 18}, {Name: "贾惜春", Age: 19}}

	o.InsertMulti(2, users)
}

func Delete() {
	var user = User{Id: 7}
	c, er := o.Delete(&user)
	if er != nil {
		fmt.Println(er)
	}
	fmt.Println(c)
}

func Update() {
	user := User{Id: 6, Name: "贾宝玉", Age: 24}
	//默认通过id 更新所有字段
	c, er := o.Update(&user)
	if er != nil {
		fmt.Println(er)
	}
	fmt.Println(c)

	user2 := User{Id: 8, Name: "史湘云", Age: 22}
	//根据id做条件只更新name字段
	o.Update(&user2, "name")
}

修改 controller 目录下 dao.go 为下面代码

package controller

import (
	"beego-demo/dao"
	"github.com/beego/beego/v2/server/web"
	"github.com/beego/beego/v2/server/web/context"
)

func RegisterDaoRoutes() {
	web.Get("/user/add", func(ctx *context.Context) {
		dao.Add()
		ctx.WriteString("添加成功")
	})

	web.Get("/user/adds", func(ctx *context.Context) {
		dao.AddMulti()
		ctx.WriteString("操作成功")
	})

	web.Get("/user/del", func(ctx *context.Context) {
		dao.Delete()
		ctx.WriteString("删除成功")
	})

	web.Get("/user/update", func(ctx *context.Context) {
		dao.Update()
		ctx.WriteString("修改成功")
	})
}

运行效果

浏览器访问:http://localhost:9090/user/update

1.5、查询

1.5.1、主键查询

方法定义为

Read(md interface{}, cols ...string) error
ReadWithCtx(ctx context.Context, md interface{}, cols ...string) error

该方法的特点是,读取到的数据会被放到 md,如果传入了 cols 参数,那么只会选取特定的列

将 dao 目录下的 user.go 修改为下面代码

package dao

import (
	"fmt"
	"github.com/beego/beego/v2/client/orm"
	//匿名引入mysql驱动
	_ "github.com/go-sql-driver/mysql"
)

type User struct {
	Id   int    `orm:"column(id)"`
	Name string `orm:"column(name)"`
	Age  int    `orm:"column(age)"`
}

var o orm.Ormer

func init() {
	// 注册 User 结构体模型
	orm.RegisterModel(new(User))

	orm.RegisterDataBase("default", "mysql", "root:123456@tcp(192.168.3.232:3306)/beego-demo-db?charset=utf8")

	orm.SetMaxIdleConns("default", 30)

	o = orm.NewOrm()
}

func Add() {
	user := new(User)
	user.Name = "贾元春"
	user.Age = 25

	// 添加数据
	o.Insert(user)
}

func AddMulti() {
	users := [2]User{{Name: "贾迎春", Age: 18}, {Name: "贾惜春", Age: 19}}

	o.InsertMulti(2, users)
}

func Delete() {
	var user = User{Id: 7}
	c, er := o.Delete(&user)
	if er != nil {
		fmt.Println(er)
	}
	fmt.Println(c)
}

func Update() {
	user := User{Id: 6, Name: "贾宝玉", Age: 24}
	//默认通过id 更新所有字段
	c, er := o.Update(&user)
	if er != nil {
		fmt.Println(er)
	}
	fmt.Println(c)

	user2 := User{Id: 8, Name: "史湘云", Age: 22}
	//根据id做条件只更新name字段
	o.Update(&user2, "name")
}

func Find() User {
	user := &User{Id: 8}
	//通过主键id 查询
	o.Read(user)
	fmt.Println(user)
	return *user
}

修改 controller 目录下 dao.go 为下面代码

package controller

import (
	"beego-demo/dao"
	"encoding/json"
	"github.com/beego/beego/v2/server/web"
	"github.com/beego/beego/v2/server/web/context"
)

func RegisterDaoRoutes() {
	web.Get("/user/add", func(ctx *context.Context) {
		dao.Add()
		ctx.WriteString("添加成功")
	})

	web.Get("/user/adds", func(ctx *context.Context) {
		dao.AddMulti()
		ctx.WriteString("操作成功")
	})

	web.Get("/user/del", func(ctx *context.Context) {
		dao.Delete()
		ctx.WriteString("删除成功")
	})

	web.Get("/user/update", func(ctx *context.Context) {
		dao.Update()
		ctx.WriteString("修改成功")
	})

	web.Get("/user/find", func(ctx *context.Context) {
		user := dao.Find()
		bytes, _ := json.Marshal(user)
		ctx.WriteString("查询成功" + string(bytes))
	})
}

运行效果

浏览器访问:http://localhost:9090/user/find

1.5.2、非主键查询

将 dao 目录下的 user.go 修改为下面代码

package dao

import (
	"fmt"
	"github.com/beego/beego/v2/client/orm"
	//匿名引入mysql驱动
	_ "github.com/go-sql-driver/mysql"
)

type User struct {
	Id   int    `orm:"column(id)"`
	Name string `orm:"column(name)"`
	Age  int    `orm:"column(age)"`
}

var o orm.Ormer

func init() {
	// 注册 User 结构体模型
	orm.RegisterModel(new(User))

	orm.RegisterDataBase("default", "mysql", "root:123456@tcp(192.168.3.232:3306)/beego-demo-db?charset=utf8")

	orm.SetMaxIdleConns("default", 30)

	o = orm.NewOrm()
}

func Add() {
	user := new(User)
	user.Name = "贾元春"
	user.Age = 25

	// 添加数据
	o.Insert(user)
}

func AddMulti() {
	users := [2]User{{Name: "贾迎春", Age: 18}, {Name: "贾惜春", Age: 19}}

	o.InsertMulti(2, users)
}

func Delete() {
	var user = User{Id: 7}
	c, er := o.Delete(&user)
	if er != nil {
		fmt.Println(er)
	}
	fmt.Println(c)
}

func Update() {
	user := User{Id: 6, Name: "贾宝玉", Age: 24}
	//默认通过id 更新所有字段
	c, er := o.Update(&user)
	if er != nil {
		fmt.Println(er)
	}
	fmt.Println(c)

	user2 := User{Id: 8, Name: "史湘云", Age: 22}
	//根据id做条件只更新name字段
	o.Update(&user2, "name")
}

func Find() User {
	//user := &User{Id: 8}
	//通过主键id 查询
	//o.Read(user)

	user := &User{Name: "贾宝玉"}
	//非主键查询
	o.Read(user, "Name")

	fmt.Println(user)
	return *user
}

重新运行

2、事务

事务依赖于 Orm 实例

ORM 操作事务,支持两种范式。一种是自己手动管理事务的生命周期;另一种通过闭包的方式,由 Beego 本身来管理事务的生命周期

2.1、手动管理事务

将 dao 目录下的 user.go 修改为下面代码

package dao

import (
	"fmt"
	"github.com/beego/beego/v2/client/orm"
	"github.com/beego/beego/v2/core/logs"

	//匿名引入mysql驱动
	_ "github.com/go-sql-driver/mysql"
)

type User struct {
	Id   int    `orm:"column(id)"`
	Name string `orm:"column(name)"`
	Age  int    `orm:"column(age)"`
}

var o orm.Ormer

func init() {
	// 注册 User 结构体模型
	orm.RegisterModel(new(User))

	orm.RegisterDataBase("default", "mysql", "root:123456@tcp(192.168.3.232:3306)/beego-demo-db?charset=utf8")

	orm.SetMaxIdleConns("default", 30)

	o = orm.NewOrm()
}

func Add() {
	user := new(User)
	user.Name = "贾元春"
	user.Age = 25

	// 添加数据
	o.Insert(user)
}

func AddMulti() {
	users := [2]User{{Name: "贾迎春", Age: 18}, {Name: "贾惜春", Age: 19}}

	o.InsertMulti(2, users)
}

func Delete() {
	var user = User{Id: 7}
	c, er := o.Delete(&user)
	if er != nil {
		fmt.Println(er)
	}
	fmt.Println(c)
}

func Update() {
	user := User{Id: 6, Name: "贾宝玉", Age: 24}
	//默认通过id 更新所有字段
	c, er := o.Update(&user)
	if er != nil {
		fmt.Println(er)
	}
	fmt.Println(c)

	user2 := User{Id: 8, Name: "史湘云", Age: 22}
	//根据id做条件只更新name字段
	o.Update(&user2, "name")
}

func Find() User {
	//user := &User{Id: 8}
	//通过主键id 查询
	//o.Read(user)

	user := &User{Name: "贾宝玉"}
	//非主键查询
	o.Read(user, "Name")

	fmt.Println(user)
	return *user
}

// TranAdd 手动管理事务的生命周期
func TranAdd() {
	to, err := o.Begin()
	if err != nil {
		logs.Error("开始事务失败")
		return
	}

	user := User{Name: "北静王", Age: 16}
	_, er := to.Insert(user)

	if er != nil {
		logs.Error("执行事务的sql失败,回滚", er)
		//回滚事物
		err = to.Rollback()
		if err != nil {
			logs.Error("回滚失败", err)
		}
	} else {
		//提交事物
		err = to.Commit()
		if err != nil {
			logs.Error("提交失败", err)
		}
	}
}

修改 controller 目录下 dao.go 为下面代码

package controller

import (
	"beego-demo/dao"
	"encoding/json"
	"github.com/beego/beego/v2/server/web"
	"github.com/beego/beego/v2/server/web/context"
)

func RegisterDaoRoutes() {
	web.Get("/user/add", func(ctx *context.Context) {
		dao.Add()
		ctx.WriteString("添加成功")
	})

	web.Get("/user/adds", func(ctx *context.Context) {
		dao.AddMulti()
		ctx.WriteString("操作成功")
	})

	web.Get("/user/del", func(ctx *context.Context) {
		dao.Delete()
		ctx.WriteString("删除成功")
	})

	web.Get("/user/update", func(ctx *context.Context) {
		dao.Update()
		ctx.WriteString("修改成功")
	})

	web.Get("/user/find", func(ctx *context.Context) {
		user := dao.Find()
		bytes, _ := json.Marshal(user)
		ctx.WriteString("查询成功" + string(bytes))
	})

	web.Get("/user/tadd", func(ctx *context.Context) {
		dao.TranAdd()
		ctx.WriteString("添加成功")
	})
}

将 user 表name 字段长度改为 25

运行效果

浏览器访问:http://localhost:9090/user/tadd

测试回滚

将 dao 目录下的 user.go 修改为下面代码

package dao

import (
	"fmt"
	"github.com/beego/beego/v2/client/orm"
	"github.com/beego/beego/v2/core/logs"

	//匿名引入mysql驱动
	_ "github.com/go-sql-driver/mysql"
)

type User struct {
	Id   int    `orm:"column(id)"`
	Name string `orm:"column(name)"`
	Age  int    `orm:"column(age)"`
}

var o orm.Ormer

func init() {
	// 注册 User 结构体模型
	orm.RegisterModel(new(User))

	orm.RegisterDataBase("default", "mysql", "root:123456@tcp(192.168.3.232:3306)/beego-demo-db?charset=utf8")

	orm.SetMaxIdleConns("default", 30)

	o = orm.NewOrm()
}

func Add() {
	user := new(User)
	user.Name = "贾元春"
	user.Age = 25

	// 添加数据
	o.Insert(user)
}

func AddMulti() {
	users := [2]User{{Name: "贾迎春", Age: 18}, {Name: "贾惜春", Age: 19}}

	o.InsertMulti(2, users)
}

func Delete() {
	var user = User{Id: 7}
	c, er := o.Delete(&user)
	if er != nil {
		fmt.Println(er)
	}
	fmt.Println(c)
}

func Update() {
	user := User{Id: 6, Name: "贾宝玉", Age: 24}
	//默认通过id 更新所有字段
	c, er := o.Update(&user)
	if er != nil {
		fmt.Println(er)
	}
	fmt.Println(c)

	user2 := User{Id: 8, Name: "史湘云", Age: 22}
	//根据id做条件只更新name字段
	o.Update(&user2, "name")
}

func Find() User {
	//user := &User{Id: 8}
	//通过主键id 查询
	//o.Read(user)

	user := &User{Name: "贾宝玉"}
	//非主键查询
	o.Read(user, "Name")

	fmt.Println(user)
	return *user
}

// TranAdd 手动管理事务的生命周期
func TranAdd() {
	to, err := o.Begin()
	if err != nil {
		logs.Error("开始事务失败")
		return
	}

	user := User{Name: "北静王123456789012346578901234564890123456790", Age: 16}
	_, er := to.Insert(&user)

	if er != nil {
		logs.Error("执行事务的sql失败,回滚", er)
		//回滚事物
		err = to.Rollback()
		if err != nil {
			logs.Error("回滚失败", err)
		}
	} else {
		//提交事物
		err = to.Commit()
		if err != nil {
			logs.Error("提交失败", err)
		}
	}
}

运行效果

2.2、闭包的方式管理事务

在这种方式里面,第一个参数是task,即该事务所有完成的动作。注意的是,如果它返回了 error,那么 Beego 会将整个事务回滚

否则提交事务

另外一个要注意的是,如果在task执行过程中,发生了panic,那么 Beego 会回滚事务

我们推荐使用这种方式

将 dao 目录下的 user.go 修改为下面代码

package dao

import (
	"fmt"
	"context"
	"github.com/beego/beego/v2/client/orm"
	"github.com/beego/beego/v2/core/logs"

	//匿名引入mysql驱动
	_ "github.com/go-sql-driver/mysql"
)

type User struct {
	Id   int    `orm:"column(id)"`
	Name string `orm:"column(name)"`
	Age  int    `orm:"column(age)"`
}

var o orm.Ormer

func init() {
	// 注册 User 结构体模型
	orm.RegisterModel(new(User))

	orm.RegisterDataBase("default", "mysql", "root:123456@tcp(192.168.3.232:3306)/beego-demo-db?charset=utf8")

	orm.SetMaxIdleConns("default", 30)

	o = orm.NewOrm()
}

func Add() {
	user := new(User)
	user.Name = "贾元春"
	user.Age = 25

	// 添加数据
	o.Insert(user)
}

func AddMulti() {
	users := [2]User{{Name: "贾迎春", Age: 18}, {Name: "贾惜春", Age: 19}}

	o.InsertMulti(2, users)
}

func Delete() {
	var user = User{Id: 7}
	c, er := o.Delete(&user)
	if er != nil {
		fmt.Println(er)
	}
	fmt.Println(c)
}

func Update() {
	user := User{Id: 6, Name: "贾宝玉", Age: 24}
	//默认通过id 更新所有字段
	c, er := o.Update(&user)
	if er != nil {
		fmt.Println(er)
	}
	fmt.Println(c)

	user2 := User{Id: 8, Name: "史湘云", Age: 22}
	//根据id做条件只更新name字段
	o.Update(&user2, "name")
}

func Find() User {
	//user := &User{Id: 8}
	//通过主键id 查询
	//o.Read(user)

	user := &User{Name: "贾宝玉"}
	//非主键查询
	o.Read(user, "Name")

	fmt.Println(user)
	return *user
}

// TranAdd 手动管理事务的生命周期
func TranAdd() {
	to, err := o.Begin()
	if err != nil {
		logs.Error("开始事务失败")
		return
	}

	user := User{Name: "北静王123456789012346578901234564890123456790", Age: 16}
	_, er := to.Insert(&user)

	if er != nil {
		logs.Error("执行事务的sql失败,回滚", er)
		//回滚事物
		err = to.Rollback()
		if err != nil {
			logs.Error("回滚失败", err)
		}
	} else {
		//提交事物
		err = to.Commit()
		if err != nil {
			logs.Error("提交失败", err)
		}
	}
}

// TranAdd2 闭包的方式,由 Beego 本身来管理事务的生命周期
func TranAdd2() {
	er := o.DoTx(func(ctx context.Context, txOrm orm.TxOrmer) error {
		user := new(User)
		user.Name = "贾政"
		user.Age = 45

		_, e := txOrm.Insert(user)

		return e
	})

	if er != nil {
		logs.Error(er)
	}
}

修改 controller 目录下 dao.go 为下面代码

package controller

import (
	"beego-demo/dao"
	"encoding/json"
	"github.com/beego/beego/v2/server/web"
	"github.com/beego/beego/v2/server/web/context"
)

func RegisterDaoRoutes() {
	web.Get("/user/add", func(ctx *context.Context) {
		dao.Add()
		ctx.WriteString("添加成功")
	})

	web.Get("/user/adds", func(ctx *context.Context) {
		dao.AddMulti()
		ctx.WriteString("操作成功")
	})

	web.Get("/user/del", func(ctx *context.Context) {
		dao.Delete()
		ctx.WriteString("删除成功")
	})

	web.Get("/user/update", func(ctx *context.Context) {
		dao.Update()
		ctx.WriteString("修改成功")
	})

	web.Get("/user/find", func(ctx *context.Context) {
		user := dao.Find()
		bytes, _ := json.Marshal(user)
		ctx.WriteString("查询成功" + string(bytes))
	})

	web.Get("/user/tadd", func(ctx *context.Context) {
		dao.TranAdd()
		ctx.WriteString("添加成功")
	})

	web.Get("/user/tadd2", func(ctx *context.Context) {
		dao.TranAdd2()
		ctx.WriteString("添加成功")
	})
}

运行效果

浏览器访问:http://localhost:9090/user/tadd2

测试回滚

将 dao 目录下的 user.go 修改为下面代码

package dao

import (
	"context"
	"fmt"
	"github.com/beego/beego/v2/client/orm"
	"github.com/beego/beego/v2/core/logs"

	//匿名引入mysql驱动
	_ "github.com/go-sql-driver/mysql"
)

type User struct {
	Id   int    `orm:"column(id)"`
	Name string `orm:"column(name)"`
	Age  int    `orm:"column(age)"`
}

var o orm.Ormer

func init() {
	// 注册 User 结构体模型
	orm.RegisterModel(new(User))

	orm.RegisterDataBase("default", "mysql", "root:123456@tcp(192.168.3.232:3306)/beego-demo-db?charset=utf8")

	orm.SetMaxIdleConns("default", 30)

	o = orm.NewOrm()
}

func Add() {
	user := new(User)
	user.Name = "贾元春"
	user.Age = 25

	// 添加数据
	o.Insert(user)
}

func AddMulti() {
	users := [2]User{{Name: "贾迎春", Age: 18}, {Name: "贾惜春", Age: 19}}

	o.InsertMulti(2, users)
}

func Delete() {
	var user = User{Id: 7}
	c, er := o.Delete(&user)
	if er != nil {
		fmt.Println(er)
	}
	fmt.Println(c)
}

func Update() {
	user := User{Id: 6, Name: "贾宝玉", Age: 24}
	//默认通过id 更新所有字段
	c, er := o.Update(&user)
	if er != nil {
		fmt.Println(er)
	}
	fmt.Println(c)

	user2 := User{Id: 8, Name: "史湘云", Age: 22}
	//根据id做条件只更新name字段
	o.Update(&user2, "name")
}

func Find() User {
	//user := &User{Id: 8}
	//通过主键id 查询
	//o.Read(user)

	user := &User{Name: "贾宝玉"}
	//非主键查询
	o.Read(user, "Name")

	fmt.Println(user)
	return *user
}

// TranAdd 手动管理事务的生命周期
func TranAdd() {
	to, err := o.Begin()
	if err != nil {
		logs.Error("开始事务失败")
		return
	}

	user := User{Name: "北静王123456789012346578901234564890123456790", Age: 16}
	_, er := to.Insert(&user)

	if er != nil {
		logs.Error("执行事务的sql失败,回滚", er)
		//回滚事物
		err = to.Rollback()
		if err != nil {
			logs.Error("回滚失败", err)
		}
	} else {
		//提交事物
		err = to.Commit()
		if err != nil {
			logs.Error("提交失败", err)
		}
	}
}

// TranAdd2 闭包的方式,由 Beego 本身来管理事务的生命周期
func TranAdd2() {
	er := o.DoTx(func(ctx context.Context, txOrm orm.TxOrmer) error {
		user := new(User)
		user.Name = "贾政123456789012345678901234567890"
		user.Age = 45

		_, e := txOrm.Insert(user)

		return e
	})

	if er != nil {
		logs.Error(er)
	}
}

运行效果

3、QueryBuilder 构造复杂查询

QueryBuilder 提供了一个简便,流畅的 SQL 查询构造器。在不影响代码可读性的前提下用来快速的建立 SQL 语句。

QueryBuilder 在功能上与 ORM 重合, 但是各有利弊。ORM 更适用于简单的 CRUD 操作,而 QueryBuilder 则更适用于复杂的查询,例如查询中包含子查询和多重联结

在 dao 目录下新建 user2.go,代码是下面内容

package dao

import (
	"fmt"
	"github.com/beego/beego/v2/client/orm"
)

func SelectUser() []User {
	qb, er := orm.NewQueryBuilder("mysql")
	if er != nil {
		panic(er)
	}

	qb.Select("id", "name").From("user").Where("id > ?")

	sql := qb.String()
	fmt.Println(sql)

	var users []User
	o.Raw(sql, 10).QueryRows(&users)

	return users
}

修改 controller 目录下 dao.go 为下面代码

package controller

import (
	"beego-demo/dao"
	"encoding/json"
	"github.com/beego/beego/v2/server/web"
	"github.com/beego/beego/v2/server/web/context"
)

func RegisterDaoRoutes() {
	web.Get("/user/add", func(ctx *context.Context) {
		dao.Add()
		ctx.WriteString("添加成功")
	})

	web.Get("/user/adds", func(ctx *context.Context) {
		dao.AddMulti()
		ctx.WriteString("操作成功")
	})

	web.Get("/user/del", func(ctx *context.Context) {
		dao.Delete()
		ctx.WriteString("删除成功")
	})

	web.Get("/user/update", func(ctx *context.Context) {
		dao.Update()
		ctx.WriteString("修改成功")
	})

	web.Get("/user/find", func(ctx *context.Context) {
		user := dao.Find()
		bytes, _ := json.Marshal(user)
		ctx.WriteString("查询成功" + string(bytes))
	})

	web.Get("/user/tadd", func(ctx *context.Context) {
		dao.TranAdd()
		ctx.WriteString("添加成功")
	})

	web.Get("/user/tadd2", func(ctx *context.Context) {
		dao.TranAdd2()
		ctx.WriteString("添加成功")
	})

	web.Get("/user/select", func(ctx *context.Context) {
		users := dao.SelectUser()
		//返回json
		ctx.Output.JSON(users, false, false)
	})
}

返回 json

运行效果,浏览器访问:http://localhost:9090/user/select

4、QuerySeter 复杂查询

ORM 以 QuerySeter 来组织查询,每个返回 QuerySeter 的方法都会获得一个新的 QuerySeter 对象

基本使用方法

o := orm.NewOrm()

// 获取 QuerySeter 对象,user 为表名
qs := o.QueryTable("user")

// 也可以直接使用 Model 结构体作为表名
qs = o.QueryTable(&User)

// 也可以直接使用对象作为表名
user := new(User)
qs = o.QueryTable(user) // 返回 QuerySeter

// 后面可以调用qs上的方法,执行复杂查询。

修改 dao 目录下 user2.go 为下面内容

package dao

import (
	"fmt"
	"github.com/beego/beego/v2/client/orm"
)

func SelectUser() []User {
	qb, er := orm.NewQueryBuilder("mysql")
	if er != nil {
		panic(er)
	}

	qb.Select("id", "name").From("user").Where("id > ?")

	sql := qb.String()
	fmt.Println(sql)

	var users []User
	o.Raw(sql, 10).QueryRows(&users)

	return users
}

func SelectUser2() User {
	o := orm.NewOrm()
	qs := o.QueryTable("user")

	var user User
	qs.Filter("id", 14).One(&user)

	return user
}

修改 controller 目录下 dao.go 为下面代码

package controller

import (
	"beego-demo/dao"
	"encoding/json"
	"github.com/beego/beego/v2/server/web"
	"github.com/beego/beego/v2/server/web/context"
)

func RegisterDaoRoutes() {
	web.Get("/user/add", func(ctx *context.Context) {
		dao.Add()
		ctx.WriteString("添加成功")
	})

	web.Get("/user/adds", func(ctx *context.Context) {
		dao.AddMulti()
		ctx.WriteString("操作成功")
	})

	web.Get("/user/del", func(ctx *context.Context) {
		dao.Delete()
		ctx.WriteString("删除成功")
	})

	web.Get("/user/update", func(ctx *context.Context) {
		dao.Update()
		ctx.WriteString("修改成功")
	})

	web.Get("/user/find", func(ctx *context.Context) {
		user := dao.Find()
		bytes, _ := json.Marshal(user)
		ctx.WriteString("查询成功" + string(bytes))
	})

	web.Get("/user/tadd", func(ctx *context.Context) {
		dao.TranAdd()
		ctx.WriteString("添加成功")
	})

	web.Get("/user/tadd2", func(ctx *context.Context) {
		dao.TranAdd2()
		ctx.WriteString("添加成功")
	})

	web.Get("/user/select", func(ctx *context.Context) {
		users := dao.SelectUser()
		//返回json
		ctx.Output.JSON(users, false, false)
	})

	web.Get("/user/select2", func(ctx *context.Context) {
		user := dao.SelectUser2()
		//返回json
		ctx.Output.JSON(user, false, false)
	})
}

运行效果

浏览器访问:http://localhost:9090/user/select2

5、原生查询

大多数时候,你都不应该使用原生查询。只有在无可奈何的情况下才应该考虑原生查询。使用原生查询可以,无需使用 ORM 表定义;多数据库,都可直接使用占位符号 ?,自动转换;查询时的参数,支持使用 Model Struct 和 Slice, Array

例如

o := orm.NewOrm()
ids := []int{1, 2, 3}
var r RawSter
r = o.Raw("SELECT name FROM user WHERE id IN (?, ?, ?)", ids)

修改 dao 目录下 user2.go 为下面内容

package dao

import (
	"fmt"
	"github.com/beego/beego/v2/client/orm"
)

func SelectUser() []User {
	qb, er := orm.NewQueryBuilder("mysql")
	if er != nil {
		panic(er)
	}

	qb.Select("id", "name").From("user").Where("id > ?")

	sql := qb.String()
	fmt.Println(sql)

	var users []User
	o.Raw(sql, 10).QueryRows(&users)

	return users
}

func SelectUser2() User {
	o := orm.NewOrm()
	qs := o.QueryTable("user")

	var user User
	qs.Filter("id", 14).One(&user)

	return user
}

// SelectUser3 原生查询
func SelectUser3() []User {
	var users []User

	ids := []int{13, 14, 15}
	r := o.Raw("SELECT id, name, age FROM user WHERE id IN (?, ?, ?)", ids)

	r.QueryRows(&users)

	//执行
	res, er := r.Exec()
	if er != nil {
		panic(er)
	}
	nums, err := res.RowsAffected()
	if err != nil {
		panic(err)
	}
	fmt.Println(nums)

	return users

}

修改 controller 目录下 dao.go 为下面代码

package controller

import (
	"beego-demo/dao"
	"encoding/json"
	"github.com/beego/beego/v2/server/web"
	"github.com/beego/beego/v2/server/web/context"
)

func RegisterDaoRoutes() {
	web.Get("/user/add", func(ctx *context.Context) {
		dao.Add()
		ctx.WriteString("添加成功")
	})

	web.Get("/user/adds", func(ctx *context.Context) {
		dao.AddMulti()
		ctx.WriteString("操作成功")
	})

	web.Get("/user/del", func(ctx *context.Context) {
		dao.Delete()
		ctx.WriteString("删除成功")
	})

	web.Get("/user/update", func(ctx *context.Context) {
		dao.Update()
		ctx.WriteString("修改成功")
	})

	web.Get("/user/find", func(ctx *context.Context) {
		user := dao.Find()
		bytes, _ := json.Marshal(user)
		ctx.WriteString("查询成功" + string(bytes))
	})

	web.Get("/user/tadd", func(ctx *context.Context) {
		dao.TranAdd()
		ctx.WriteString("添加成功")
	})

	web.Get("/user/tadd2", func(ctx *context.Context) {
		dao.TranAdd2()
		ctx.WriteString("添加成功")
	})

	web.Get("/user/select", func(ctx *context.Context) {
		users := dao.SelectUser()
		//返回json
		ctx.Output.JSON(users, false, false)
	})

	web.Get("/user/select2", func(ctx *context.Context) {
		user := dao.SelectUser2()
		//返回json
		ctx.Output.JSON(user, false, false)
	})

	web.Get("/user/select3", func(ctx *context.Context) {
		users := dao.SelectUser3()
		//返回json
		ctx.Output.JSON(users, false, false)
	})
}

运行效果

浏览器访问:http://localhost:9090/user/select3

更多详情用法请查阅 Beego 官方文档

下一讲:《Beego 使用教程 11:缓存》

至此完

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

悟世君子

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值