使用 orm 实现增删改查 – beego 框架使用笔记
2012 年 6 月 10 日
beego ORM 是一个强大的 Go 语言 ORM 框架,目前该框架已支持 MySQL、PostgreSQL、Sqlite3 数据库驱动。
安装 ORM
go get github.com/astaxie/beego/orm 复制代码
初始化
创建项目
使用 bee 工具的安装 api 命令 bee api apiproject
创建 apiproject 项目,具体使用方法查看 beego api 命令
。
模型关系
# 外键始终在子表上 # 一个用户对应一个简介;一个简介对应一个用户; one2one:User(子表) -> Profile(主表);one2one:Profile -> User 复制代码
结构体
type User struct { Id int `json:"id"` Username string `json:"username"` Password string `json:"password"` Profile *Profile `orm:"rel(one)" json:"profile"` // OneToOne relation } type Profile struct { Id int `json:"id"` Gender int `json:"gender"` Age int `json:"age"` Address string `json:"address"` Email string `json:"email"` User *User `orm:"reverse(one)" json:"-"` // 设置一对一反向关系(可选) } 复制代码
使用标签`orm:"column(id)`对属性进行标注,用于解析。 `orm:"rel(one)"` 表示one2one `orm:"reverse(one)"` `orm:"reverse(one)"` 标注反向关系 复制代码
database 信息
注册 model:
orm.RegisterModel(new(User), new(Profile)) 复制代码
自定义表名:
func (u *User) TableName() string { return "users" } func (u *Profile) TableName() string { return "users_profiles" } 复制代码
自动建表:
orm.RunSyncdb("default", false, true) 复制代码
数据表结构:
mysql> show tables; +-------------------+ | Tables_in_play_db | +-------------------+ | users | | users_profiles | +-------------------+ 2 rows in set (0.01 sec) mysql> desc users; +------------+--------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +------------+--------------+------+-----+---------+----------------+ | id | int(11) | NO | PRI | NULL | auto_increment | | username | varchar(255) | NO | | | | | password | varchar(255) | NO | | | | | profile_id | int(11) | NO | UNI | NULL | | +------------+--------------+------+-----+---------+----------------+ 4 rows in set (0.02 sec) mysql> desc users_profiles; +---------+--------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +---------+--------------+------+-----+---------+----------------+ | id | int(11) | NO | PRI | NULL | auto_increment | | gender | int(11) | YES | | 0 | | | age | int(11) | YES | | 0 | | | address | varchar(255) | YES | | | | | email | varchar(255) | YES | | | | +---------+--------------+------+-----+---------+----------------+ 5 rows in set (0.01 sec) 复制代码
功能实现
项目参数配置
/apiproject/conf/app.conf
appname = apiproject httpport = 8090 runmode = dev autorender = false copyrequestbody = true EnableDocs = true sqlconn = root:123456@/play_db?charset=utf8 复制代码
通用 utils
分页:
/apiproject/utils/page.go
package utils var ( PageSize int = 10 ) type Page struct { Page int `json:"page"` PageSize int `json:"pageSize"` TotalPage int `json:"totalPage"` TotalCount int `json:"totalCount"` FirstPage bool `json:"firstPage"` LastPage bool `json:"lastPage"` List interface{} `json:"list"` } func Pagination(count int, page int, pageSize int, list interface{}) Page { tp := count / pageSize if count%pageSize > 0 { tp = count/pageSize + 1 } return Page{Page: page, PageSize: pageSize, TotalPage: tp, TotalCount: count, FirstPage: page == 1, LastPage: page == tp, List: list} } 复制代码
main 入口
/apiproject/main.go
package main import ( _ "apiproject/routers" "github.com/astaxie/beego/orm" "github.com/astaxie/beego" _ "github.com/go-sql-driver/mysql" ) func init() { // 参数1 driverName // 参数2 数据库类型 // 这个用来设置 driverName 对应的数据库类型 // mysql / sqlite3 / postgres 这三种是默认已经注册过的,所以可以无需设置 // orm.RegisterDriver("mysql", orm.DRMySQL) // ORM 必须注册一个别名为 default 的数据库,作为默认使用。 // 参数1 数据库的别名,用来在 ORM 中切换数据库使用 // 参数2 driverName // 参数3 对应的链接字符串 orm.RegisterDataBase("default", "mysql", beego.AppConfig.String("sqlconn")) } func main() { if beego.BConfig.RunMode == "dev" { beego.BConfig.WebConfig.DirectoryIndex = true beego.BConfig.WebConfig.StaticDir["/swagger"] = "swagger" } // 开启 orm 调试模式:开发过程中建议打开,release时需要关闭 orm.Debug = true beego.Run() } 复制代码
路由实现
RESTful Controller 路由,初始化 namespace:
/apiproject/routers/router.go
// @APIVersion 1.0.0 // @Title apiproject API // @License Apache 2.0 package routers import ( "apiproject/controllers" "github.com/astaxie/beego" ) func init() { ns := beego.NewNamespace("/v1", beego.NSNamespace("/object", beego.NSInclude( &controllers.ObjectController{}, ), ), beego.NSNamespace("/user", beego.NSInclude( &controllers.UserController{}, ), ), ) beego.AddNamespace(ns) } 复制代码
/apiproject/routers/commentsRouter_controllers.go
package routers import ( "github.com/astaxie/beego" "github.com/astaxie/beego/context/param" ) func init() { beego.GlobalControllerRouter["apiproject/controllers:UserController"] = append(beego.GlobalControllerRouter["apiproject/controllers:UserController"], beego.ControllerComments{ Method: "Post", Router: `/`, AllowHTTPMethods: []string{"post"}, MethodParams: param.Make(), Filters: nil, Params: nil}) beego.GlobalControllerRouter["apiproject/controllers:UserController"] = append(beego.GlobalControllerRouter["apiproject/controllers:UserController"], beego.ControllerComments{ Method: "GetAll", Router: `/`, AllowHTTPMethods: []string{"get"}, MethodParams: param.Make(), Filters: nil, Params: nil}) beego.GlobalControllerRouter["apiproject/controllers:UserController"] = append(beego.GlobalControllerRouter["apiproject/controllers:UserController"], beego.ControllerComments{ Method: "Get", Router: `/:uid`, AllowHTTPMethods: []string{"get"}, MethodParams: param.Make(), Filters: nil, Params: nil}) beego.GlobalControllerRouter["apiproject/controllers:UserController"] = append(beego.GlobalControllerRouter["apiproject/controllers:UserController"], beego.ControllerComments{ Method: "Put", Router: `/:uid`, AllowHTTPMethods: []string{"put"}, MethodParams: param.Make(), Filters: nil, Params: nil}) beego.GlobalControllerRouter["apiproject/controllers:UserController"] = append(beego.GlobalControllerRouter["apiproject/controllers:UserController"], beego.ControllerComments{ Method: "Delete", Router: `/:uid`, AllowHTTPMethods: []string{"delete"}, MethodParams: param.Make(), Filters: nil, Params: nil}) } 复制代码
controller 实现
通用 controller base:
/apiproject/controllers/base.go
package controllers import ( "github.com/astaxie/beego" ) type BaseController struct { beego.Controller } // Response 结构体 type Response struct { Code int `json:"code"` Message string `json:"message"` Data interface{} `json:"data"` } // Error Response 结构体 type ErrResponse struct { Code int `json:"code"` Message string `json:"message"` } 复制代码
自定义错误提示码:
/apiproject/controllers/code.go
package controllers var ( // Common errors SUCCESS = &Errno{Code: 0, Message: "成功"} InternalServerError = &Errno{Code: 10001, Message: "内部服务错误"} ErrBind = &Errno{Code: 10002, Message: "参数错误"} ErrDatabase = &Errno{Code: 20001, Message: "数据库错误"} ErrToken = &Errno{Code: 20002, Message: "签发令牌出错"} ErrNoPermission = &Errno{Code: 20003, Message: "无权限"} // user errors ErrUserNotFound = &Errno{Code: 20101, Message: "用户未注册"} ErrUserExist = &Errno{Code: 20102, Message: "用户已存在"} ) 复制代码
通用错误处理:
/apiproject/controllers/errno.go
package controllers import "fmt" type Errno struct { Code int Message string } func (err Errno) Error() string { return err.Message } // Err represents an error type Err struct { Code int Message string Err error } func New(errno *Errno, err error) *Err { return &Err{Code: errno.Code, Message: errno.Message, Err: err} } func (err *Err) Add(message string) error { err.Message += " " + message return err } func (err *Err) Addf(format string, args ...interface{}) error { err.Message += " " + fmt.Sprintf(format, args...) return err } func (err *Err) Error() string { return fmt.Sprintf("Err - code: %d, message: %s, error: %s", err.Code, err.Message, err.Err) } func IsErrUserNotFound(err error) bool { code, _ := DecodeErr(err) return code == ErrUserNotFound.Code } func DecodeErr(err error) (int, string) { if err == nil { return SUCCESS.Code, SUCCESS.Message } switch typed := err.(type) { case *Err: return typed.Code, typed.Message case *Errno: return typed.Code, typed.Message default: } return InternalServerError.Code, err.Error() } 复制代码
用户 controller 模块:
/apiproject/controllers/user.go
package controllers import ( "apiproject/models" "apiproject/utils" "encoding/json" "strconv" ) // Operations about Users type UserController struct { BaseController } // @Title CreateUser // @Description create users // @Param body body models.User true "body for user content" // @Success 200 {int} models.User.Id // @Failure 403 body is empty // @router / [post] func (u *UserController) Post() { var user models.User _ = json.Unmarshal(u.Ctx.Input.RequestBody, &user) uid, _ := models.AddUser(user) u.Data["json"] = map[string]int64{"uid": uid} u.ServeJSON() } // @Title GetAll // @Description get all Users // @Success 200 {object} models.User // @router / [get] func (u *UserController) GetAll() { currentPage, _ := strconv.Atoi(u.Ctx.Input.Query("page")) if currentPage == 0 { currentPage = 1 } pageSize := utils.PageSize d , err:= models.GetAllUsers(currentPage,pageSize) code, message := DecodeErr(err) if err != nil { u.Data["json"] = ErrResponse{code, message} } else { u.Data["json"] = Response{code, message, d} } u.ServeJSON() } // @Title Get // @Description get user by uid // @Param uid path string true "The key for staticblock" // @Success 200 {object} models.User // @Failure 403 :uid is empty // @router /:uid [get] func (u *UserController) Get() { uid, _ := u.GetInt(":uid") if uid > 0 { user, err := models.GetUser(uid) code, message := DecodeErr(err) if err != nil { u.Data["json"] = ErrResponse{code, message} } else { u.Data["json"] = Response{code, message, user} } } u.ServeJSON() } // @Title Update // @Description update the user // @Param uid path string true "The uid you want to update" // @Param body body models.User true "body for user content" // @Success 200 {object} models.User // @Failure 403 :uid is not int // @router /:uid [put] func (u *UserController) Put() { uid, _ := u.GetInt(":uid") if uid > 0 { var user models.User _ = json.Unmarshal(u.Ctx.Input.RequestBody, &user) uu, err := models.UpdateUser(uid, &user) code, message := DecodeErr(err) if err != nil { u.Data["json"] = ErrResponse{code, message} } else { u.Data["json"] = Response{code, message, uu} } } u.ServeJSON() } // @Title Delete // @Description delete the user // @Param uid path string true "The uid you want to delete" // @Success 200 {string} delete success! // @Failure 403 uid is empty // @router /:uid [delete] func (u *UserController) Delete() { uid, _ := u.GetInt(":uid") b, err := models.DeleteUser(uid) code, message := DecodeErr(err) if err != nil { u.Data["json"] = ErrResponse{code, message} } else { u.Data["json"] = Response{code, message, b} } u.ServeJSON() } 复制代码
model 实现
用户 model 模块,orm 实现 增删改查:
/apiproject/models/user.go
package models import ( "apiproject/utils" "errors" "github.com/astaxie/beego/orm" "strconv" ) type User struct { Id int `json:"id"` Username string `json:"username"` Password string `json:"password"` Profile *Profile `orm:"rel(one)" json:"profile"` // OneToOne relation } type Profile struct { Id int `json:"id"` Gender int `json:"gender"` Age int `json:"age"` Address string `json:"address"` Email string `json:"email"` User *User `orm:"reverse(one)" json:"-"` // 设置一对一反向关系(可选) } // 自定义表名 func (u *User) TableName() string { return "users" } func (u *Profile) TableName() string { return "users_profiles" } // 新增用户 func AddUser(u User) (id int64, err error) { // one2one 插入 // 创建一个 ormer 对象 o := orm.NewOrm() // 开启事务 err = o.Begin() // 插入主表 profile := Profile{ Gender: u.Profile.Gender, Age: u.Profile.Age, Address: u.Profile.Address, Email: u.Profile.Email} id, err = o.Insert(&profile) if err != nil { // 回滚事务 err = o.Rollback() } // 插入子表 user := User{ Username: u.Username, Password: u.Password, Profile: &Profile{Id: int(id)}} _, err = o.Insert(&user) if err != nil { // 回滚事务 err = o.Rollback() } // 提交事务 err = o.Commit() return id, err } // 查询指定用户 func GetUser(uid int) (u *User, err error) { o := orm.NewOrm() user := &User{Id: uid} err = o.Read(user) // 已经取得了 Users 对象,查询 UserProfiles if user.Profile != nil { err = o.Read(user.Profile) } return user, err } // 分页查询用户 func GetAllUsers(p int, size int) (u utils.Page, err error) { o := orm.NewOrm() user := new(User) var users []User qs := o.QueryTable(user) count, _ := qs.Limit(-1).Count() _, err = qs.RelatedSel().Limit(size).Offset((p - 1) * size).All(&users) for _, u := range users { if u.Profile != nil { err = o.Read(u.Profile) } } c, _ := strconv.Atoi(strconv.FormatInt(count, 10)) return utils.Pagination(c, p, size, users), err } // 更新指定用户 func UpdateUser(uid int, uu *User) (a *User, err error) { o := orm.NewOrm() user := User{Id: uid} profile := Profile{Id: uid} if o.Read(&user) == nil { if uu.Username != "" { user.Username = uu.Username } if uu.Password != "" { user.Password = uu.Password } if o.Read(&profile) == nil { if uu.Profile.Age > 0 { profile.Age = uu.Profile.Age } if uu.Profile.Address != "" { profile.Address = uu.Profile.Address } if uu.Profile.Gender == 0 || uu.Profile.Gender == 1 { profile.Gender = uu.Profile.Gender } if uu.Profile.Email != "" { profile.Email = uu.Profile.Email } } user.Profile = &profile // 开启事务 err = o.Begin() if _, err := o.Update(&user); err != nil { return nil, errors.New("修改失败") } if _, err := o.Update(&profile); err != nil { return nil, errors.New("修改失败") } if err != nil { err = o.Rollback() } else { err = o.Commit() } return &user, nil } return nil, err } // 删除指定用户 func DeleteUser(uid int) (b bool, err error) { // one2one 删除 // 创建一个 ormer 对象 o := orm.NewOrm() // 开启事务 err = o.Begin() // 删除主表 profile := Profile{Id: uid} _, err = o.Delete(&profile) if err != nil { // 回滚事务 err = o.Rollback() } // 删除子表 user := User{Id: uid} _, err = o.Delete(&user) if err != nil { // 回滚事务 err = o.Rollback() } // 提交事务 err = o.Commit() return b, err } // 注册 model func init(){ orm.RegisterModel(new(User), new(Profile)) } 复制代码