楔子
实际上前文提到beego对国人比较友好的意思是,因为beego虽然是台湾人开发的,但是他就职于盛大(没错,就是热血传奇的那个游戏公司,在上海的台湾人还真是多啊,就是酒量不怎么样...),因此beego拥有其他框架都没有的中文文档,而且是先有中文文档、后有英文文档,某种程度上英文文档会滞后于中文文档,赞。
不过由于beego的开发文档是按启动顺序和模块来写的,看起来较为枯燥,我的学习顺序是按实现过程来写的,过程中遇到某个功能再返回文档中查验,因此虽然理论上应该先看参数配置和路由设置,不过我们还是跳过去先来看看如何通过数据库操作配合控制器和模版引擎来实现简单的增删改查操作。
model
我们都知道model在web应用中是进行数据持久化的操作,当然这只是约定俗成的事情,如果你非要把MVC的所有文件都扔到一个包里面也不是不可以,只不过那种感觉就像是8.0版本的萨满,你非要玩增强萨谁能拦着你?
在beego当中,作者编写了一个ORM(Object-relational mapping)框架来处理数据库的相关操作,ORM翻成中文就是对象关系映射,简单的说,ORM是通过使用描述对象和数据库之间映射的元数据,将程序中的对象自动持久化到关系数据库中。
在beego ORM的框架下,可以便捷的通过结构体来新增、更新表以及对表内数据进行增删改查操作,并且不需要写SQL语句。当然通过ORM带来方便的同时,也会在一定程度上降低系统执行效率,同时,自动档开多了可能会削弱你手动档操作的能力,如何取舍见仁见智吧。
beego ORM操作
beego ORM同时支持三种数据库:MySQL、PostgreSQL、Sqlite3,我们这里以MySQL举例,由于Go语言原生并不包含数据库驱动,要使用MySQL,首先我们必须先下载一个MySQL的驱动,这里我们使用应用最广的Go-MySQL-Driver
安装驱动:go get -u github.com/go-sql-driver/mysql
新建数据库
首先你需要在MySQL数据库中开一个实例,比如test
然后设置其编码格式为:
Default collation: utf8mb4_unicode_ci
Default characterset: utf8mb4
然后我们在model文件夹里面创建一个model.go
文件,来进行数据库连接和结构体创建(在实际项目当中,这些配置应该写在app.conf
里面,并且最好的实现方式是将函数构造题和数据库同步单独来写,这里我们先写在一起,主要体会一下beego ORM的操作流程):
package models
import "github.com/astaxie/beego/orm"
import _"github.com/go-sql-driver/mysql"
type User struct {
Id int
Name string
Pwd string
}
func init() {
//设置数据库参数
orm.RegisterDataBase("default", "mysql", "[user]:[pwd]@tcp([ipAdress]:3306)/test?charset=utf8")
//建立model映射关系
orm.RegisterModel(new(User))
//执行同步数据库
orm.RunSyncdb("default", false, true)
}
我们来分析一下上面的源码:
- 首先,我们引用了ORM包便于使用内部的操作函数,然后我们引入了mysql驱动来连接数据库,数据库包前面的下划线不能省略,因为我们只需要执行这个包里面的init函数来装载数据库驱动,并没有调用里面的函数,按照Go语言的强制要求,不使用函数调用会报错;
- 然后我们构建一个名为User的结构体,用于和数据库之间建立映射关系并进行数据操作;
- 设置数据库连接、建立数据映射关系、执行同步数据库
在init函数当中,前面两个配置比较好理解,在执行orm.RunSyncdb
的时候我们有必要跟进去看一下是如何实现的:
// RunSyncdb run syncdb command line.
// name means table's alias name. default is "default".
// force means run next sql if the current is error.
// verbose means show all info when running command or not.
func RunSyncdb(name string, force bool, verbose bool) error {
BootStrap()
al := getDbAlias(name)
cmd := new(commandSyncDb)
cmd.al = al
cmd.force = force
cmd.noInfo = !verbose
cmd.verbose = verbose
cmd.rtOnError = true
return cmd.Run()
}
orm.RunSyncdb
包含了三个参数name
、force
、verbose
name是数据库别名,需要跟数据库配置当中的别名保持一致
force代表强制更新,也就是先drop table再进行建表,建议设置为false除非在系统更新过程中,前提就是你需要把表中数据备份再写入
verbose代表显示信息,设置为true的时候,执行过程中对数据库的操作会在后台打印出详细信息
由于我们建表的过程是写在init函数中,因此接下来我们只要在系统执行过程中import这个包,那么在系统运行的时候就会自动建立。(在实际项目中,通常会放在一个类似_initSys
的包里面,但是我们这里写在controller
里面好了)
controller
在beego学习笔记一当中我们了解到,从主函数进来后进行了路由注册,而路由又指向了对应的控制器,下面来看一下之前使用bee工具自动生成的控制器代码:
package controllers
import (
"github.com/astaxie/beego"
)
type MainController struct {
beego.Controller
}
func (c *MainController) Get() {
c.Data["Website"] = "beego.me"
c.Data["Email"] = "[email protected]"
c.TplName = "index.tpl"
}
在这段代码里,用户通过浏览器访问web应用根目录时,会塞两个参数(Website、Email)到data当中,并通过模版引擎封装最终呈现给用户,我们就在这里添加构建数据库表的方法。
首先在import当中添加如下代码:
import (
"github.com/astaxie/beego"
"github.com/astaxie/beego/orm"
"quickstart/models"
)
对比上面的代码,添加了orm组件和model文件,也就是说,在系统启动的时候,系统就会自动执行建表操作,构建user表:
Insert
//new一个ORM对象
o := orm.NewOrm()
//拿到要插入的结构体对象
user := models.User{}
//对结构体对象进行赋值
user.Name = "张三"
user.Pwd = "123456"
//执行插入操作
id,err := o.Insert(&user)
//处理报错信息
if err != nil{
beego.Info("插入失败",err)
return
}
beego.Info("插入成功",id)
实现Insert操作比较简单,看一下上面的源码,每一行我都写了注释,需要注意的是由于我们创建表的时候id字段自动设置为自增变量,因此在实际项目当中,插入字段返回的id值其实可以保存起来为将来做多表关联查询使用,这里就不做处理了。
Read
//new一个ORM对象
o := orm.NewOrm()
//拿到要读取的结构体对象
user := models.User{}
//设置查询条件,就是SQL里面where t.name=“张三”
user.Name = "张三"
//执行查询操作
err := o.Read(&user,"Name")
//处理报错信息
if err != nil{
beego.Info("查询失败",err)
return
}
beego.Info("查询成功",user)
读取操作和插入操作类似,需要注意的是,如果使用主键查询,那么就不需要设置查询条件,在执行查询操作的时候可以省略后面的查询条件字段,直接写err := o.Read(&user)
即可。
Update
//new一个ORM对象
o := orm.NewOrm()
//拿到要读取的结构体对象
user := models.User{}
//设置更新字段ID
user.Id = 1
//找到需要更新的字段
err := o.Read(&user)
//如果找到字段再进行更新操作
if err == nil{
//设置需要更新的内容
user.Name = "李四"
//执行更新操作
num,err := o.Update(&user)
//处理报错信息
if err != nil{
beego.Info("更新失败",err)
return
}
beego.Info("更新成功",num)
}
//...这里可以再设置一个找不到字段的报错信息
更新操作没什么可说的,需要注意的是执行更新操作返回的num是影响的行数
Delete
//new一个ORM对象
o := orm.NewOrm()
//拿到要读取的结构体对象
user := models.User{}
//设置更新字段ID
user.Id = 1
//执行删除操作
num,err := o.Delete(&user)
//处理报错信息
if err != nil{
beego.Info("删除失败",err)
return
}
更新操作没什么可说的,需要注意的是执行删除操作返回的num是影响的行数,同时ORM支持关联删除,这个我们后面再研究。
view
这里我们先做一个简单到令人发指的注册页面,view提交后由controller接收参数,然后进行业务操作,最终调用model写入数据库。
注册
注册页面
这里一个form表单,里面包含了两个输入框和一个按钮,当点击提交按钮时,模版引擎会把两个input里面的值保存在模版引擎的实例当中(其实就是指针对应的内存空间里)
func (c *MainController) Post() {
//拿到view实例
c.TplName = "test.html"
//获取view提交的
userName := c.GetString("userName")
pwd := c.GetString("pwd")
//new一个ORM对象
o := orm.NewOrm()
//拿到要插入的结构体对象
user := models.User{}
//对结构体对象进行赋值
user.Name = userName
user.Pwd = pwd
//执行插入操作
id,err := o.Insert(&user)
//处理报错信息
if err != nil{
beego.Info("插入失败",err)
return
}
beego.Info("插入成功",id)
}
controller里面使用c.GetString来拿到内存中的具体值,然后就可以使用我们上面谈到的Insert方法进行数据库操作了,插入成功后可以将反馈信息以c.Data["err"] = err的形式再返回给前端用户
在实际项目当中,我们还需要进行数据校验、防止恶意注册等操作,日后再说。