# 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 ``` 例子3,WHERE 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 ``` 例子2,limit預設從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 * [目錄]() * 上一節: [使用PostgreSQL資料庫](<05.4.md>) * 下一節: [NOSQL資料庫操作](<05.6.md>)