1. Model介绍
beego ORM 是一个强大的 Go 语言 ORM 框架,orm模块主要是处理MVC中的M(models)
对象-关系映射(Object-Relational Mapping,简称ORM)。当我们实现一个应用程序时(不使用O/R Mapping),我们可能会写特别多数据访问层的代码,从数据库保存、删除、读取对象信息,而这些代码都是重复的。而使用ORM则会大大减少重复性代码。
ORM的特性:
- 支持范围广:支持 Go 的所有类型存储
- 上手容易:采用简单的 CRUD 风格,允许直接使用 SQL 查询/映射
- 兼容性好:跨数据库兼容查询
- 自动 Join 关联表
2. 基本应用
2.1 数据库设置
首先需要安装orm模块和mysql驱动:
// 安装beego orm包
go get github.com/astaxie/beego/orm
// 安装mysql驱动
go get github.com/go-sql-driver/mysql
同时,也需要导入包:
import (
// 导入orm包
"github.com/astaxie/beego/orm"
// 导入mysql驱动
_ "github.com/go-sql-driver/mysql"
)
接下来是配置连接信息,配置过程和GoWeb实战1方法相似:
const(
userName = "test"
password = "asdfg12345"
ip ="cdb-axt937vt.gz.tencentcdb.com"
port="10059"
dbName = "test"
)
func init(){
connectInfo:=[]string{userName,":",password,"@tcp(",ip,":",port,")/", dbName, "?charset=utf8&parseTime=true&loc=Local"}
path:=strings.Join(connectInfo,"")
orm.RegisterDataBase("default","mysql",path)
// 打开调试模式,开发的时候方便查看orm生成什么样子的sql语句
orm.Debug=true
}
数据库注册函数的原型如下:
func RegisterDataBase(aliasName, driverName, dataSource string, params ...int) error
参数名 | 说明 |
---|---|
aliasName | 数据库的别名,用来在 ORM 中切换数据库使用 |
driverName | 驱动名字 |
dataSource | 数据库连接字符串 |
params | 附加参数 |
常用的附加参数有:
参数名 | 默认值 | 说明 |
---|---|---|
charset | none | 设置字符集,相当于 SET NAMES |
loc | UTC | 设置时区,可以设置为Local,表示根据本地时区走 |
parseTime | false | 是否需要将 mysql的 DATE 和 DATETIME 类型值转换成GO的time.Time类型。 |
readTimeout | 0 | I/O 读超时时间, sql查询超时时间. 单位 (“ms”, “s”, “m”, “h”), 例子: “30s”, “0.5m” or “1m30s”. |
timeout | 0 | 连接超时时间,单位(“ms”, “s”, “m”, “h”), 例子: “30s”, “0.5m” or “1m30s”. |
2.2 准备工作
首先我们创建一个表,用于测试:
CREATE TABLE `online_orders` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增ID',
`shop_id` int(10) unsigned NOT NULL COMMENT '店铺id',
`customer_id` int(10) unsigned NOT NULL COMMENT '用户id',
`nickname` varchar(20) DEFAULT NULL COMMENT '用户昵称',
`address` varchar(200) NOT NULL DEFAULT '' COMMENT '用户地址',
`init_time` datetime NOT NULL COMMENT '创建订单的时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
id为无符号整形,长度最长为10,不允许空NOT NULL
,自增。varchar
是可变字符串类型。datetime
类型用于存储时间,存储方式是:
YYYY-MM-DD HH:MM:SS
建完后可以看到:
接下来需要构建结构体承载数据:
type Order struct{
Id int
ShopId int
UserId int `orm:"colunm(customer_id)"`
Nickname string
Address string
InitTime time.Time
}
ORM中结构体和表明的转化规则是驼峰转蛇形,除了开头以外,遇到大写需要_
代替:
AuthUser -> auth_user
Auth_User -> auth__user
DB_AuthUser -> d_b__auth_user
所以,ShopId
对应的是shop_id
,又因为UserId
在表中对应的是customer_id
,不匹配,所以需要通过orm标签指定表字段名。
然后我们再为结构体添加一个函数,指定Order结构体默认绑定的表名:
func (o *Order) TableName() string {
return "orders"
}
最后在main函数中,注册模型
orm.RegisterModel(new(Order))
除了前面提到的用column设置名字外,还有其他有用的参数设置:
- 忽略字段
-
- 设置自增键
auto
- 设置为主键
pk
- 在数据库默认非空下,允许为空
null
比如:
type User struct {
...
AnyField string `orm:"-"`
...
}
2.3 CRUD操作
(1)插入数据
单条插入:
orm.RegisterModel(new(Order))
o := orm.NewOrm()
order:=Order{
ShopId:1,
UserId:1002,
Nickname:"BOB",
Address: "北京东路",
InitTime:time.Now(),
}
id,err:=o.Insert(&order)
if err != nil {
fmt.Println("插入失败,err:",err)
} else {
// 插入成功会返回插入数据自增字段,生成的id
fmt.Println("新插入数据的id为:", id)
}
批量插入:
o := orm.NewOrm()
orders := []Order{
{ShopId:1, UserId:1001, Nickname:"大锤1", Address:"深圳南山区", InitTime: time.Now()},
{ShopId:1, UserId:1002, Nickname:"大锤2", Address:"深圳南山区", InitTime: time.Now()},
{ShopId:1, UserId:1003, Nickname:"大锤3", Address:"深圳南山区", InitTime: time.Now()},
}
// 调用InsertMulti函数批量插入, 第一个参数指的是要插入多少数据
nums, err := o.InsertMulti(3, orders)
(2)更新数据
更新所有字段:
orm.RegisterModel(new(Order))
o:=orm.NewOrm()
order:=Order{}
order.Id=1
order.Nickname="希特"
order.Address="四川资阳"
order.InitTime=time.Now()
num,err:=o.Update(&order)
//shop_id和custom_id将会变为0
if err != nil {
fmt.Println("更新失败,err:",err)
} else {
fmt.Println("更新数据影响的行数:", num)
}
更新指定字段:
num, err := o.Update(&order, "Nickname", "Address")
(3)查询数据
orm.RegisterModel(new(Order))
o:=orm.NewOrm()
order:=Order{}
// 先对主键id赋值, 查询数据的条件就是where id=2
order.Id=1
err:=o.Read(&order)
if err == orm.ErrNoRows {
fmt.Println("查询不到")
} else if err == orm.ErrMissPK {
fmt.Println("找不到主键")
} else {
fmt.Println(order.Id, order.Nickname)
}
(4)删除数据
orm.RegisterModel(new(Order))
o := orm.NewOrm()
order := Order{}
// 先对主键id赋值, 删除数据的条件就是where id=2
order.Id = 1
if num, err := o.Delete(&order); err != nil {
fmt.Println("删除失败")
} else {
fmt.Println("删除数据影响的行数:", num)
}
3. 高级查询
首先重新定义一个用户表
CREATE TABLE `users` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增ID',
`username` varchar(30) NOT NULL COMMENT '账号',
`password` varchar(100) NOT NULL COMMENT '密码',
`city` varchar(50) DEFAULT NULL COMMENT '城市',
`init_time` datetime NOT NULL COMMENT '创建的时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
然后再定义结构体:
type User struct{
Id int
Username string
Password string
City string
InitTime time.Time
}
func (o *User) TableName() string{
return "users"
}
3.1 过滤查询
过滤查询主要用的是QuerySeter
,下面是一个简单的条件查询,查询来自武汉的且生成时间晚于2020年的用户:
qs:=o.QueryTable("users")
var users []User
num,err:=qs.Filter("city","WUHAN").Filter(("init_time__gt", "2020-01-01 22:00:00").All(&users,"id","username")
if err!=nil{
panic(err)
}
fmt.Println("结果行数:", num)
可以看到Filter能连携使用,还能处理大于小于等条件。使用等于条件时:
qs.Filter("id", 1) // 相当于条件 id = 1
使用不等条件时,用双下划线 __ 作为分隔符,尾部连接操作符:
qs.Filter("id__gt", 1) // 相当于条件 id > 1
qs.Filter("id__gte", 1) // 相当于条件 id >= 1
qs.Filter("id__lt", 1) // 相当于条件 id < 1
qs.Filter("id__lte", 1) // 相当于条件 id <= 1
qs.Filter("id__in", 1,2,3,4,5) // 相当于In语句 id in (1,2,3,4,5)
此外还有其他操作符:
- exact / iexact 等于,i表示大小写不敏感
- contains / icontains 包含,i表示大小写不敏感
- gt / gte 大于 / 大于等于
- lt / lte 小于 / 小于等于
- startswith / istartswith 以…起始
- endswith / iendswith 以…结束
- in 在其中
- isnull
比如:
qs.Filter("Username__icontains", "大锤") // 相当于条件 name LIKE '%大锤%'
qs.Filter("profile__age__in", ids)
// WHERE profile.age IN (17, 18, 19, 20)
3.2 复杂查询
有的时候我们需要将条件组合起来查询,上面的例子多个Filter函数调用只能生成and连接的查询条件。beego orm为我们提供了Condition对象,用于生成查询条件。
// 创建一个Condition对象
cond := orm.NewCondition()
// 组织查询条件, 并返回一个新的Condition对象
cond1 := cond.And("Id__gt", 100).Or("City","shenzhen")
// 相当于条件 id > 100 or city = 'shenzhen'
var users []User
qs.SetCond(cond1). // 设置查询条件
Limit(10). // 限制返回数据函数
All(&users) // 查询多行数据
此外还可以连协Count,Group等功能:
num, _ := o.QueryTable("users").Filter("Id__gt", 1).Filter("Id__lt", 100).Count()
num, err := qs.Filter("Id__gt", 1).
Filter("Id__lt", 100).
GroupBy("City"). // 根据city字段分组
OrderBy("-InitTime"). // order by字段名前面的减号 - , 代表倒序。
Limit(10). // 限制返回行数
All(&users)
4. 事务
// 创建orm对象
o := orm.NewOrm()
// 开始事务
o.Begin()
// 开始执行各种sql语句,更新数据库,这里可以使用beego orm支持任何一种方式操作数据库
// 例如,更新订单状态
_, err1 := o.QueryTable("orders").Filter("Id", 1001).Update(orm.Params{
"Status": "SUCCESS",
})
// 给用户加积分
_, err2 := o.Raw("update users set points = points + ? where username=?", "tizi365", 100).Exec()
// 检测事务执行状态
if err1 != nil || err2 != nil {
// 如果执行失败,回滚事务
o.Rollback()
} else {
// 任务执行成功,提交事务
o.Commit()
}