1. 程式人生 > 程式設計 >使用 orm 實現增刪改查 - beego 框架使用筆記

使用 orm 實現增刪改查 - beego 框架使用筆記

概述

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",AllowHTTPMethods: []string{"get"},beego.ControllerComments{
            Method: "Get",Router: `/:uid`,beego.ControllerComments{
            Method: "Put",AllowHTTPMethods: []string{"put"},beego.ControllerComments{
            Method: "Delete",AllowHTTPMethods: []string{"delete"},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,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,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,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))
}
複製程式碼

END

以上示例實現使用者模組的增、刪、改、查應用介面 API 功能開發。