Files
build-web-application-with-…/zh-tw/05.5.md
2019-02-26 01:40:54 +08:00

449 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 5.5 使用Beego orm函式庫進行ORM開發
beego orm是我開發的一個Go進行ORM操作的函式庫它採用了Go style方式對資料庫進行操作實現了struct到資料表記錄的對映。beego orm是一個十分輕量級的Go ORM框架開發這個函式庫的本意降低複雜的ORM學習曲線儘可能在ORM的執行效率和功能之間尋求一個平衡beego orm是目前開源的Go ORM框架中實現比較完整的一個函式庫而且執行效率相當不錯功能也基本能滿足需求。
beego orm是支援database/sql標準介面的ORM函式庫所以理論上來說只要資料庫驅動支援database/sql介面就可以無縫的接入beego orm。目前我測試過的驅動包括下面幾個
Mysql: [github/go-mysql-driver/mysql](https://github.com/go-sql-driver/mysql)
PostgreSQL: [github.com/lib/pq](https://github.com/lib/pq)
SQLite: [github.com/mattn/go-sqlite3](https://github.com/mattn/go-sqlite3)
Mysql: [github.com/ziutek/mymysql/godrv](https://github.com/ziutek/mymysql)
暫未支援資料庫:
MsSql: [github.com/denisenkom/go-mssqldb](https://github.com/denisenkom/go-mssqldb)
MS ADODB: [github.com/mattn/go-adodb](https://github.com/mattn/go-adodb)
Oracle: [github.com/mattn/go-oci8](https://github.com/mattn/go-oci8)
ODBC: [bitbucket.org/miquella/mgodbc](https://bitbucket.org/miquella/mgodbc)
## 安裝
beego orm支援go get方式安裝是完全按照Go Style的方式來實現的。
go get github.com/astaxie/beego
## 如何初始化
首先你需要import相應的資料庫驅動套件、database/sql標準介面套件以及beego orm套件如下所示
```Go
import (
"database/sql"
"github.com/astaxie/beego/orm"
_ "github.com/go-sql-driver/mysql"
)
func init() {
//註冊驅動
orm.RegisterDriver("mysql", orm.DR_MySQL)
//設定預設資料庫
orm.RegisterDataBase("default", "mysql", "root:root@/my_db?charset=utf8", 30)
//註冊定義的model
orm.RegisterModel(new(User))
// 建立table
orm.RunSyncdb("default", false, true)
}
```
PostgreSQL 配置:
```Go
//匯入驅動
// _ "github.com/lib/pq"
// 註冊驅動
orm.RegisterDriver("postgres", orm.DR_Postgres)
// 設定預設資料庫
//PostgresQL使用者postgres 密碼zxxx 資料庫名稱test 資料庫別名default
orm.RegisterDataBase("default", "postgres", "user=postgres password=zxxx dbname=test host=127.0.0.1 port=5432 sslmode=disable")
```
MySQL 配置:
```Go
//匯入驅動
//_ "github.com/go-sql-driver/mysql"
//註冊驅動
orm.RegisterDriver("mysql", orm.DR_MySQL)
// 設定預設資料庫
//mysql使用者root 密碼zxxx 資料庫名稱test 資料庫別名default
orm.RegisterDataBase("default", "mysql", "root:zxxx@/test?charset=utf8")
```
Sqlite 配置:
```Go
//匯入驅動
//_ "github.com/mattn/go-sqlite3"
//註冊驅動
orm.RegisterDriver("sqlite", orm.DR_Sqlite)
// 設定預設資料庫
//資料庫存放位置:./datas/test.db 資料庫別名default
orm.RegisterDataBase("default", "sqlite3", "./datas/test.db")
```
匯入必須的package之後,我們需要開啟到資料庫的連結然後建立一個beego orm物件以MySQL為例),如下所示
beego orm:
```Go
func main() {
o := orm.NewOrm()
}
```
簡單示例:
```Go
package main
import (
"fmt"
"github.com/astaxie/beego/orm"
_ "github.com/go-sql-driver/mysql" // 匯入資料庫驅動
)
// Model Struct
type User struct {
Id int
Name string `orm:"size(100)"`
}
func init() {
// 設定預設資料庫
orm.RegisterDataBase("default", "mysql", "root:root@/my_db?charset=utf8", 30)
// 註冊定義的 model
orm.RegisterModel(new(User))
//RegisterModel 也可以同時註冊多個 model
//orm.RegisterModel(new(User), new(Profile), new(Post))
// 建立 table
orm.RunSyncdb("default", false, true)
}
func main() {
o := orm.NewOrm()
user := User{Name: "slene"}
// 插入表
id, err := o.Insert(&user)
fmt.Printf("ID: %d, ERR: %v\n", id, err)
// 更新表
user.Name = "astaxie"
num, err := o.Update(&user)
fmt.Printf("NUM: %d, ERR: %v\n", num, err)
// 讀取 one
u := User{Id: user.Id}
err = o.Read(&u)
fmt.Printf("ERR: %v\n", err)
// 刪除表
num, err = o.Delete(&u)
fmt.Printf("NUM: %d, ERR: %v\n", num, err)
}
```
SetMaxIdleConns
根據資料庫的別名,設定資料庫的最大空閒連線
```Go
orm.SetMaxIdleConns("default", 30)
```
SetMaxOpenConns
根據資料庫的別名,設定資料庫的最大資料庫連線 (go >= 1.2)
```Go
orm.SetMaxOpenConns("default", 30)
```
目前beego orm支援列印除錯你可以透過如下的程式碼實現除錯
```Go
orm.Debug = true
```
接下來我們的例子採用前面的資料庫表User現在我們建立相應的struct
```Go
type Userinfo struct {
Uid int `PK` //如果表的主鍵不是id那麼需要加上pk註釋顯式的說這個欄位是主鍵
Username string
Departname string
Created time.Time
}
type User struct {
Uid int `PK` //如果表的主鍵不是id那麼需要加上pk註釋顯式的說這個欄位是主鍵
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)"`
}
func init() {
// 需要在init中註冊定義的model
orm.RegisterModel(new(Userinfo),new(User), new(Profile), new(Tag))
}
```
>注意一點beego orm針對駝峰命名會自動幫你轉化成下劃線欄位例如你定義了Struct名字為`UserInfo`,那麼轉化成底層實現的時候是`user_info`,欄位命名也遵循該規則。
## 插入資料
下面的程式碼示範瞭如何插入一條記錄可以看到我們操作的是struct物件而不是原生的sql語句最後透過呼叫Insert介面將資料儲存到資料庫。
```Go
o := orm.NewOrm()
var user User
user.Name = "zxxx"
user.Departname = "zxxx"
id, err := o.Insert(&user)
if err == nil {
fmt.Println(id)
}
```
我們看到插入之後`user.Uid`就是插入成功之後的自增ID。
同時插入多個物件:InsertMulti
類似sql語句
```Go
insert into table (name, age) values("slene", 28),("astaxie", 30),("unknown", 20)
```
第一個引數 bulk 為並列插入的數量第二個為物件的slice
返回值為成功插入的數量
```Go
users := []User{
{Name: "slene"},
{Name: "astaxie"},
{Name: "unknown"},
...
}
successNums, err := o.InsertMulti(100, users)
```
bulk 為 1 時,將會順序插入 slice 中的資料
## 更新資料
繼續上面的例子來示範更新操作現在user的主鍵已經有值了此時呼叫Insert介面beego orm內部會自動呼叫update以進行資料的更新而非插入操作。
```Go
o := orm.NewOrm()
user := User{Uid: 1}
if o.Read(&user) == nil {
user.Name = "MyName"
if num, err := o.Update(&user); err == nil {
fmt.Println(num)
}
}
```
Update 預設更新所有的欄位,可以更新指定的欄位:
```Go
// 只更新 Name
o.Update(&user, "Name")
// 指定多個欄位
// o.Update(&user, "Field1", "Field2", ...)
```
//Where:用來設定條件支援多個引數第一個引數如果為整數相當於呼叫了Where("主鍵=?",值)。
## 查詢資料
beego orm的查詢介面比較靈活具體使用請看下面的例子
例子1根據主鍵取得資料
```Go
o := orm.NewOrm()
var user User
user := User{Id: 1}
err = o.Read(&user)
if err == orm.ErrNoRows {
fmt.Println("查詢不到")
} else if err == orm.ErrMissPK {
fmt.Println("找不到主鍵")
} else {
fmt.Println(user.Id, user.Name)
}
```
例子2
```Go
o := orm.NewOrm()
var user User
qs := o.QueryTable(user) // 返回 QuerySeter
qs.Filter("id", 1) // WHERE id = 1
qs.Filter("profile__age", 18) // WHERE profile.age = 18
```
例子3WHERE IN查詢條件
```Go
qs.Filter("profile__age__in", 18, 20)
// WHERE profile.age IN (18, 20)
```
例子4更加複雜的條件
```Go
qs.Filter("profile__age__in", 18, 20).Exclude("profile__lt", 1000)
// WHERE profile.age IN (18, 20) AND NOT profile_id < 1000
```
可以透過如下介面取得多條資料,請看示例
例子1根據條件age>17取得20位置開始的10條資料的資料
```Go
var allusers []User
qs.Filter("profile__age__gt", 17)
// WHERE profile.age > 17
```
例子2limit預設從10開始取得10條資料
```Go
qs.Limit(10, 20)
// LIMIT 10 OFFSET 20 注意跟SQL反過來的
```
## 刪除資料
beedb提供了豐富的刪除資料介面請看下面的例子
例子1刪除單條資料
```Go
o := orm.NewOrm()
if num, err := o.Delete(&User{Id: 1}); err == nil {
fmt.Println(num)
}
```
Delete 操作會對反向關係進行操作,此例中 Post 擁有一個到 User 的外來鍵。刪除 User 的時候。如果 on_delete 設定為預設的級聯操作,將刪除對應的 Post
## 關聯查詢
有些應用卻需要用到連線查詢所以現在beego orm提供了一個簡陋的實現方案
```Go
type Post struct {
Id int `orm:"auto"`
Title string `orm:"size(100)"`
User *User `orm:"rel(fk)"`
}
var posts []*Post
qs := o.QueryTable("post")
num, err := qs.Filter("User__Name", "slene").All(&posts)
```
上面程式碼中我們看到了一個struct關聯查詢
## Group By和Having
針對有些應用需要用到group by的功能beego orm也提供了一個簡陋的實現
```Go
qs.OrderBy("id", "-profile__age")
// ORDER BY id ASC, profile.age DESC
qs.OrderBy("-profile__age", "profile")
// ORDER BY profile.age DESC, profile_id ASC
```
上面的程式碼中出現了兩個新介面函式
GroupBy:用來指定進行groupby的欄位
Having:用來指定having執行的時候的條件
## 使用原生sql
簡單示例:
```Go
o := orm.NewOrm()
var r orm.RawSeter
r = o.Raw("UPDATE user SET name = ? WHERE name = ?", "testing", "slene")
```
複雜原生sql使用:
```Go
func (m *User) Query(name string) user []User {
var o orm.Ormer
var rs orm.RawSeter
o = orm.NewOrm()
rs = o.Raw("SELECT * FROM user "+
"WHERE name=? AND uid>10 "+
"ORDER BY uid DESC "+
"LIMIT 100", name)
//var user []User
num, err := rs.QueryRows(&user)
if err != nil {
fmt.Println(err)
} else {
fmt.Println(num)
//return user
}
return
}
```
更多說明,請到[beego.me](https://beego.me)
## 進一步的發展
目前beego orm已經獲得了很多來自國內外使用者的反饋我目前也正在考慮支援更多資料庫接下來會在更多方面進行改進
## links
* [目錄](<preface.md>)
* 上一節: [使用PostgreSQL資料庫](<05.4.md>)
* 下一節: [NOSQL資料庫操作](<05.6.md>)