1. 程式人生 > >ChainDesk-Beego之ORM模型Model介紹

ChainDesk-Beego之ORM模型Model介紹

作者:ChainDesk韓茹,ChainDesk區塊鏈行業分析師, ChainDesk區塊鏈工程師
ChainDesk官網:http://www.chaindesk.cn/?20181205csdnmeiti
本篇文章閱讀時間:3.6分鐘

模型(Models)

一、Model介紹

beego ORM 是一個強大的 Go 語言 ORM 框架,orm模組主要是處理MVC中的M(models)。她的靈感主要來自Django ORM 和 SQLAlchemy。

物件關係對映(Object Relational Mapping,簡稱ORM)模式是一種為了解決面向物件與關係資料庫存在的互不匹配的現象的技術。簡單的說,ORM是通過使用描述物件和資料庫之間對映的元資料,將程式中的物件自動持久化到關係資料庫中。

已支援資料庫驅動:

  • MySQL : github. com/go-sq-driver/mysql
  • PostgreSQL : github. com/lib/pq
  • Sqlite3 : github. com/mattn/go-sqite3

模型(Models)
ORM特性:

  • 支援Go的所有型別儲存
  • 輕鬆上手,採用簡單的CRUD風格
  • 自動Join關聯表
  • 跨資料庫相容查詢
  • 允許直接使用SQL查詢/對映
  • 嚴格完整的測試保證ORM的穩定與健壯

1.1 模型定義

複雜的模型定義不是必須的,此功能用作資料庫資料轉換和自動建表

預設的表名規則,使用駝峰轉蛇形:

AuthUser -> auth_user
Auth_User -> auth__user
DB_AuthUser -> d_b__auth_user

除了開頭的大寫字母以外,遇到大寫會增加 _,原名稱中的下劃線保留。

1.2 自定義表名

type User struct {
    Id int
    Name string
}

func (u *User) TableName() string {
    return "auth_user"
}

如果字首設定prefix_那麼表名為:prefix_auth_user

1.3 忽略欄位

設定 -

即可忽略 struct 中的欄位

type User struct {
...
    Username string `orm:"column(username)"`
    AnyField string `orm:"-"`
...
}

結構體和表對映時,可以自動對映,也可以通過orm:column(列名)來指定。

二、安裝

安裝ORM:

開啟終端,輸入以下命令:

go get github.com/astaxie/beego/orm

安裝成功後,可以檢視gopath的src目錄:

anzhuang3

三、ORM的使用

ORM的使用非常簡單,只有三步:

step1:註冊(初始化資訊)

step2:建立Model(構建struct實體)

step3:資料庫操作

我們先通過終端進入gopath的src目錄下,使用如下命令,建立一個beego專案:

bee new beegoORM

3.1 註冊

3.1.1 註冊資料庫資訊

3.1.1.1 RegisterDriver

三種預設資料庫型別

// For version 1.6
orm.DRMySQL
orm.DRSqlite
orm.DRPostgres

// < 1.6
orm.DR_MySQL
orm.DR_Sqlite
orm.DR_Postgres

註冊驅動的語法:

// 引數1   driverName
// 引數2   資料庫型別
// 這個用來設定 driverName 對應的資料庫型別
// mysql / sqlite3 / postgres 這三種是預設已經註冊過的,所以可以無需設定
orm.RegisterDriver("mysql", orm.DRMySQL) 
3.1.1.2 RegisterDataBase

ORM 必須註冊一個別名為 default 的資料庫,作為預設使用。

ORM 使用 golang 自己的連線池。

// 引數1        資料庫的別名,用來在 ORM 中切換資料庫使用
// 引數2        driverName
// 引數3        對應的連結字串
orm.RegisterDataBase("default", "mysql", "root:[email protected]/orm_test?charset=utf8")

// 引數4(可選)  設定最大空閒連線
// 引數5(可選)  設定最大資料庫連線 (go >= 1.2)
maxIdle := 30
maxConn := 30
orm.RegisterDataBase("default", "mysql", "root:[email protected]/orm_test?charset=utf8", maxIdle, maxConn)

3.1.1.3 SetMaxIdleConns

根據資料庫的別名,設定資料庫的最大空閒連線。

orm.SetMaxIdleConns("default", 30)
3.1.1.4 SetMaxOpenConns

根據資料庫的別名,設定資料庫的最大資料庫連線 (go >= 1.2)

orm.SetMaxOpenConns("default", 30)
3.1.1.5 時區設定

ORM 預設使用 time.Local 本地時區

  • 作用於 ORM 自動建立的時間
  • 從資料庫中取回的時間轉換成 ORM 本地時間

如果需要的話,你也可以進行更改

// 設定為 UTC 時間
orm.DefaultTimeLoc = time.UTC

ORM 在進行 RegisterDataBase 的同時,會獲取資料庫使用的時區,然後在 time.Time 型別存取時做相應轉換,以匹配時間系統,從而保證時間不會出錯。

注意:

  • 鑑於 Sqlite3 的設計,存取預設都為 UTC 時間
  • 使用 go-sql-driver 驅動時,請注意引數設定
    從某一版本開始,驅動預設使用 UTC 時間,而非本地時間,所以請指定時區引數或者全部以 UTC 時間存取
    例如:root:[email protected]/orm_test?charset=utf8&loc=Asia%2FShanghai
    參見 loc / parseTime

3.1.2 註冊模型

如果使用 orm.QuerySeter 進行高階查詢的話,這個是必須的。

反之,如果只使用 Raw 查詢和 map struct,是無需這一步的。您可以去檢視 Raw SQL 查詢

3.1.2.1 RegisterModel

將你定義的 Model 進行註冊,最佳設計是有單獨的 models.go 檔案,在他的 init 函式中進行註冊。

迷你版 models.go

package main

import "github.com/astaxie/beego/orm"

type User struct {
    Id   int
    Name string
}

func init(){
    orm.RegisterModel(new(User))
}

RegisterModel 也可以同時註冊多個 model

orm.RegisterModel(new(User), new(Profile), new(Post))

詳細的 struct 定義請檢視文件 模型定義

3.1.2.2 RegisterModelWithPrefix

使用表名字首

orm.RegisterModelWithPrefix("prefix_", new(User))

建立後的表名為 prefix_user

3.1.2.3 NewOrmWithDB

有時候需要自行管理連線池與資料庫連結(比如:go 的連結池無法讓兩次查詢使用同一個連結的)

但又想使用 ORM 的查詢功能

var driverName, aliasName string
// driverName 是驅動的名稱
// aliasName 是當前 db 的自定義別名
var db *sql.DB
...
o := orm.NewOrmWithDB(driverName, aliasName, db)
3.1.2.4 GetDB

從已註冊的資料庫返回 *sql.DB 物件,預設返回別名為 default 的資料庫。

db, err := orm.GetDB()
if err != nil {
    fmt.Println("get default DataBase")
}

db, err := orm.GetDB("alias")
if err != nil {
    fmt.Println("get alias DataBase")
}
3.1.2.5 ResetModelCache

重置已經註冊的模型 struct,一般用於編寫測試用例

orm.ResetModelCache()

3.1.3 示例程式碼

新建一個Controller,新建go檔案(testmodel.go)。

匯入包

將你需要使用的driver假如import中:

import (
    _ "github.com/go-sql-driver/mysql"
    _ "github.com/lib/pq"
    _ "github. com/mattn/go-sqlite3"

我們可以在init()方法中進行註冊:

/**
  初始化db,註冊預設資料庫,同時將實體模型也註冊上去
 */
func init() {
	//註冊驅動:如果是預設的三個可以不寫
	orm.RegisterDriver("mysql", orm.DRMySQL) //可以省略不寫

	//註冊預設資料庫,ORM 必須註冊一個別名為default的資料庫,作為預設使用。
    /*
	引數一:資料庫別名
	引數二:驅動名稱
	引數三:資料庫連線字串:username:[email protected](127.0.0.1:3306)/databasename?charset=utf8
	引數四:設定資料庫的最大空閒連線
	*/
	orm.RegisterDataBase("default", "mysql", "root:[email protected](127.0.0.1:3306)/mytest?charset=utf8", 30)
}

3.2 建立Model

3.2.1 自動建表

//自動建表
orm.RunSyncdb("default", false, true)

以下是官方文件的案例:

3.2.1.1 關係模型
# 外來鍵始終在子表上

#一個使用者對應一個簡介;一個簡介對應一個使用者;
one2one:User(子表) -> Profile(主表);one2one:Profile -> User 

#一個郵件對應一個使用者;一個使用者有多個郵件;
one2many:Post(子表) -> User(主表);many2one:User -> Post 

#一個郵件對應多個標籤;一個標籤對應多個郵件;
many2many:Post(子表) -> Tag(主表);many2many:Tag -> Post 

說明:

使用標籤`orm:"column(id)`對屬性進行標註,用於解析。
標註邏輯外來鍵,自動建表時不會生成外來鍵。
`orm:"rel(one)"` 表示one2one
`orm:"rel(fk)"`  表示one2many
`orm:"rel(m2m)"` 表示many2many
`orm:"reverse(one)"` `orm:"reverse(one)"`  標註反向關係
3.2.1.2 建立結構體
//-----------定義struct-------------

type User struct { // 對應user表
	Id      int
	Name    string
	Profile *Profile `orm:"rel(one)"`      // OneToOne relation
	Post    []*Post  `orm:"reverse(many)"` // 設定一對多的反向關係
}

type Profile struct {
	Id   int
	Age  int16
	User *User `orm:"reverse(one)"` // 設定一對一反向關係(可選)
}

type Post struct {
	Id    int
	Title string
	User  *User  `orm:"rel(fk)"` //設定一對多關係
	Tags  []*Tag `orm:"rel(m2m)"`
}

type Tag struct {
	Id    int
	Name  string
	Posts []*Post `orm:"reverse(many)"`
}

3.2.2 註冊Model

在init()方法 中,添加註冊model程式碼:

func init() {
	orm.RegisterDriver("mysql", orm.DRMySQL) //可以不加
	orm.RegisterDataBase("default", "mysql", "root:[email protected](127.0.0.1:3306)/mytest?charset=utf8", 30)

	// 需要在init中註冊定義的model
	orm.RegisterModel(new(User), new(Post), new(Profile), new(Tag)
}

3.2.3 示例程式碼:

修改router.go檔案,註冊路由:

func init() {
    beego.Router("/", &controllers.MainController{})
    beego.Router("/testcreatetable", &controllers.ModelController{},"get:CreateTable")
}

在testmodel.go中,新增對應路由的方法:

func (c *ModelController) CreateTable(){
	//自動建表
	orm.RunSyncdb("default", false, true)
	datainit()
}

我們先新增一個初始化資訊的方法,關於CRUD的部分,後面會詳細講解:

func datainit() {
	o:=orm.NewOrm()
	 //rel  : 自動生成外來鍵為 表名_id
	sql1 := "insert into user (name,profile_id) values ('hanru',1),('ruby',2),('王二狗',3);"
	sql2 := "insert into profile (age) values (20),(19),(21);"
	sql3 := "insert into tag (name) values ('offical'),('beta'),('dev');"
	sql4 := "insert into post (title,user_id) values ('paper1',1),('paper2',1),('paper3',2),('paper4',3),('paper5',3);"
	// m2m 生成的 表名:子表_主表s  主鍵自增
	sql5 := "insert into post_tags (tag_id, post_id) values (1,1),(1,3),(2,2),(3,3),(2,4),(3,4),(3,5); "

	//使用Raw().Exec()執行sql
	o.Raw(sql1).Exec()
	o.Raw(sql2).Exec()
	o.Raw(sql3).Exec()
	o.Raw(sql4).Exec()
	o.Raw(sql5).Exec()
}

啟動專案後,開啟瀏覽器輸入:http://127.0.0.1:8080/testcreatetable執行,然後重新整理資料庫,我們發現已經建立好了5張資料表:

createtable1

接下來我們查詢一下表結構:

createtable2

3.2.4 database生成規則

1.新增rel(one)、rel(fk)的屬性會生成 表名_id的欄位,rel(one)時生成欄位key為unique

type User struct {
    Profile *Profile `orm:"rel(one)"`
}

type Profile struct {
    User *User `orm:"reverse(one)"`  //可以省略
}
---->
mysql> desc user;
+------------+--------------+------+-----+---------+----------------+
| Field      | Type         | Null | Key | Default | Extra          |
+------------+--------------+------+-----+---------+----------------+
| id         | int(11)      | NO   | PRI | NULL    | auto_increment |
| name       | varchar(255) | NO   |     |         |                |
| profile_id | int(11)      | NO   | UNI | NULL    |                |
+------------+--------------+------+-----+---------+----------------+

2.rel(m2m)、reverse(many)這一對欄位 會生成關係對應表 子表_主表s 主鍵自增

type Post struct {
    Tags  []*Tag `orm:"rel(m2m)"` // m2m relation
}

type Tag struct {
    Posts []*Post `orm:"reverse(many)"`
}
---->
mysql> desc post_tags;
+---------+------------+------+-----+---------+----------------+
| Field   | Type       | Null | Key | Default | Extra          |
+---------+------------+------+-----+---------+----------------+
| id      | bigint(20) | NO   | PRI | NULL    | auto_increment |
| post_id | int(11)    | NO   |     | NULL    |                |
| tag_id  | int(11)    | NO   |     | NULL    |                |
+---------+------------+------+-----+---------+----------------+

接下來我們查詢一下表中的資料:

1.user表:

userbiao

2.profile表:

profilebiao

3.post表:

postbiao

4.tag表:

tagbiao

5.post_tags表:

post_tags

3.3 ORM 介面使用

使用 ORM 必然接觸的 Ormer 介面,我們來熟悉一下

var o Ormer
o = orm.NewOrm() // 建立一個 Ormer
// NewOrm 的同時會執行 orm.BootStrap (整個 app 只執行一次),用以驗證模型之間的定義並快取。

切換資料庫,或者,進行事務處理,都會作用於這個 Ormer 物件,以及其進行的任何查詢。

所以:需要 切換資料庫事務處理 的話,不要使用全域性儲存的 Ormer 物件。

3.3.1 QueryTable

傳入表名,或者 Model 物件,返回一個 QuerySeter

o := orm.NewOrm()
var qs QuerySeter
qs = o.QueryTable("user")
// 如果表沒有定義過,會立刻 panic

3.3.2 Using

切換為其他資料庫

orm.RegisterDataBase("db1", "mysql", "root:[email protected]/orm_db2?charset=utf8")
orm.RegisterDataBase("db2", "sqlite3", "data.db")

o1 := orm.NewOrm()
o1.Using("db1")

o2 := orm.NewOrm()
o2.Using("db2")

// 切換為其他資料庫以後
// 這個 Ormer 物件的其下的 api 呼叫都將使用這個資料庫

預設使用 default 資料庫,無需呼叫 Using

3.3.3 Raw

使用 sql 語句直接進行操作

Raw 函式,返回一個 RawSeter 用以對設定的 sql 語句和引數進行操作

o := NewOrm()
var r RawSeter
r = o.Raw("UPDATE user SET name = ? WHERE name = ?", "testing", "slene")

3.3.4 Driver

返回當前 ORM 使用的 db 資訊:

type Driver interface {
    Name() string
    Type() DriverType
}

示例程式碼:


orm.RegisterDataBase("db1", "mysql", "root:[email protected]/orm_db2?charset=utf8")
orm.RegisterDataBase("db2", "sqlite3", "data.db")

o1 := orm.NewOrm()
o1.Using("db1")
dr := o1.Driver()
fmt.Println(dr.Name() == "db1") // true
fmt.Println(dr.Type() == orm.DRMySQL) // true

o2 := orm.NewOrm()
o2.Using("db2")
dr = o2.Driver()
fmt.Println(dr.Name() == "db2") // true
fmt.Println(dr.Type() == orm.DRSqlite) // true

關於資料庫的具體操作,我們下一章節繼續講解。

ChainDesk,是一個學習方式
chaindesk.cn,首創基於區塊鏈的多相腦圖分割模型學習社群
獨創 專業 系統 高效是我們的代名詞,學你想學,想你所想
在這裡,找到你的小夥伴,一起學習,一同成長