beego orm是我開發(fā)的一個(gè)Go進(jìn)行ORM操作的庫,它采用了Go style方式對數(shù)據(jù)庫進(jìn)行操作,實(shí)現(xiàn)了struct到數(shù)據(jù)表記錄的映射。beego orm是一個(gè)十分輕量級的Go ORM框架,開發(fā)這個(gè)庫的本意降低復(fù)雜的ORM學(xué)習(xí)曲線,盡可能在ORM的運(yùn)行效率和功能之間尋求一個(gè)平衡,beego orm是目前開源的Go ORM框架中實(shí)現(xiàn)比較完整的一個(gè)庫,而且運(yùn)行效率相當(dāng)不錯(cuò),功能也基本能滿足需求。
beego orm是支持database/sql標(biāo)準(zhǔn)接口的ORM庫,所以理論上來說,只要數(shù)據(jù)庫驅(qū)動(dòng)支持database/sql接口就可以無縫的接入beego orm。目前我測試過的驅(qū)動(dòng)包括下面幾個(gè):
Mysql: github/go-mysql-driver/mysql
PostgreSQL: github.com/lib/pq
SQLite: github.com/mattn/go-sqlite3
Mysql: github.com/ziutek/mymysql/godrv
暫未支持?jǐn)?shù)據(jù)庫:
MsSql: github.com/denisenkom/go-mssqldb
MS ADODB: github.com/mattn/go-adodb
Oracle: github.com/mattn/go-oci8
ODBC: bitbucket.org/miquella/mgodbc
beego orm支持go get方式安裝,是完全按照Go Style的方式來實(shí)現(xiàn)的。
go get github.com/astaxie/beego
首先你需要import相應(yīng)的數(shù)據(jù)庫驅(qū)動(dòng)包、database/sql標(biāo)準(zhǔn)接口包以及beego orm包,如下所示:
import (
"database/sql"
"github.com/astaxie/beego/orm"
_ "github.com/go-sql-driver/mysql"
)
func init() {
//注冊驅(qū)動(dòng)
orm.RegisterDriver("mysql", orm.DRMySQL)
//設(shè)置默認(rèn)數(shù)據(jù)庫
orm.RegisterDataBase("default", "mysql", "root:root@/my_db?charset=utf8", 30)
//注冊定義的model
orm.RegisterModel(new(User))
// 創(chuàng)建table
orm.RunSyncdb("default", false, true)
}
PostgreSQL 配置:
//導(dǎo)入驅(qū)動(dòng)
// _ "github.com/lib/pq"
// 注冊驅(qū)動(dòng)
orm.RegisterDriver("postgres", orm.DR_Postgres)
// 設(shè)置默認(rèn)數(shù)據(jù)庫
//PostgresQL用戶:postgres ,密碼:zxxx , 數(shù)據(jù)庫名稱:test , 數(shù)據(jù)庫別名:default
orm.RegisterDataBase("default", "postgres", "user=postgres password=zxxx dbname=test host=127.0.0.1 port=5432 sslmode=disable")
MySQL 配置:
//導(dǎo)入驅(qū)動(dòng)
//_ "github.com/go-sql-driver/mysql"
//注冊驅(qū)動(dòng)
orm.RegisterDriver("mysql", orm.DR_MySQL)
// 設(shè)置默認(rèn)數(shù)據(jù)庫
//mysql用戶:root ,密碼:zxxx , 數(shù)據(jù)庫名稱:test , 數(shù)據(jù)庫別名:default
orm.RegisterDataBase("default", "mysql", "root:zxxx@/test?charset=utf8")
Sqlite 配置:
//導(dǎo)入驅(qū)動(dòng)
//_ "github.com/mattn/go-sqlite3"
//注冊驅(qū)動(dòng)
orm.RegisterDriver("sqlite", orm.DR_Sqlite)
// 設(shè)置默認(rèn)數(shù)據(jù)庫
//數(shù)據(jù)庫存放位置:./datas/test.db , 數(shù)據(jù)庫別名:default
orm.RegisterDataBase("default", "sqlite3", "./datas/test.db")
導(dǎo)入必須的package之后,我們需要打開到數(shù)據(jù)庫的鏈接,然后創(chuàng)建一個(gè)beego orm對象(以MySQL為例),如下所示 beego orm:
func main() {
o := orm.NewOrm()
}
簡單示例:
package main
import (
"fmt"
"github.com/astaxie/beego/orm"
_ "github.com/go-sql-driver/mysql" // 導(dǎo)入數(shù)據(jù)庫驅(qū)動(dòng)
)
// Model Struct
type User struct {
Id int
Name string `orm:"size(100)"`
}
func init() {
// 設(shè)置默認(rèn)數(shù)據(jù)庫
orm.RegisterDataBase("default", "mysql", "root:root@/my_db?charset=utf8", 30)
// 注冊定義的 model
orm.RegisterModel(new(User))
//RegisterModel 也可以同時(shí)注冊多個(gè) model
//orm.RegisterModel(new(User), new(Profile), new(Post))
// 創(chuàng)建 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
根據(jù)數(shù)據(jù)庫的別名,設(shè)置數(shù)據(jù)庫的最大空閑連接
orm.SetMaxIdleConns("default", 30)
SetMaxOpenConns
根據(jù)數(shù)據(jù)庫的別名,設(shè)置數(shù)據(jù)庫的最大數(shù)據(jù)庫連接 (go >= 1.2)
orm.SetMaxOpenConns("default", 30)
目前beego orm支持打印調(diào)試,你可以通過如下的代碼實(shí)現(xiàn)調(diào)試
orm.Debug = true
接下來我們的例子采用前面的數(shù)據(jù)庫表User,現(xiàn)在我們建立相應(yīng)的struct
type Userinfo struct {
Uid int `orm:"PK"` //如果表的主鍵不是id,那么需要加上pk注釋,顯式的說這個(gè)字段是主鍵
Username string
Departname string
Created time.Time
}
type User struct {
Uid int `orm:"PK"` //如果表的主鍵不是id,那么需要加上pk注釋,顯式的說這個(gè)字段是主鍵
Name string
Profile *Profile `orm:"rel(one)"` // OneToOne relation
Post []*Post `orm:"reverse(many)"` // 設(shè)置一對多的反向關(guān)系
}
type Profile struct {
Id int
Age int16
User *User `orm:"reverse(one)"` // 設(shè)置一對一反向關(guān)系(可選)
}
type Post struct {
Id int
Title string
User *User `orm:"rel(fk)"`
Tags []*Tag `orm:"rel(m2m)"` //設(shè)置一對多關(guān)系
}
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(Post), new(Tag))
}
注意一點(diǎn),beego orm針對駝峰命名會自動(dòng)幫你轉(zhuǎn)化成下劃線字段,例如你定義了struct名字為?
UserInfo
?,那么轉(zhuǎn)化成底層實(shí)現(xiàn)的時(shí)候是?user_info
?,字段命名也遵循該規(guī)則。
下面的代碼演示了如何插入一條記錄,可以看到我們操作的是struct對象,而不是原生的sql語句,最后通過調(diào)用Insert接口將數(shù)據(jù)保存到數(shù)據(jù)庫。
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。
同時(shí)插入多個(gè)對象:InsertMulti
類似sql語句
insert into table (name, age) values("slene", 28),("astaxie", 30),("unknown", 20)
第一個(gè)參數(shù) bulk 為并列插入的數(shù)量,第二個(gè)為對象的slice
返回值為成功插入的數(shù)量
users := []User{
{Name: "slene"},
{Name: "astaxie"},
{Name: "unknown"},
...
}
successNums, err := o.InsertMulti(100, users)
bulk 為 1 時(shí),將會順序插入 slice 中的數(shù)據(jù)
繼續(xù)上面的例子來演示更新操作,現(xiàn)在user的主鍵已經(jīng)有值了,此時(shí)調(diào)用Insert接口,beego orm內(nèi)部會自動(dòng)調(diào)用update以進(jìn)行數(shù)據(jù)的更新而非插入操作。
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 默認(rèn)更新所有的字段,可以更新指定的字段:
// 只更新 Name
o.Update(&user, "Name")
// 指定多個(gè)字段
// o.Update(&user, "Field1", "Field2", ...)
Where: 用來設(shè)置條件,支持多個(gè)參數(shù),第一個(gè)參數(shù)如果為整數(shù),相當(dāng)于調(diào)用了Where("主鍵=?",值)。
beego orm的查詢接口比較靈活,具體使用請看下面的例子
例子1,根據(jù)主鍵獲取數(shù)據(jù):
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:
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查詢條件:
qs.Filter("profile__age__in", 18, 20)
// WHERE profile.age IN (18, 20)
例子4,更加復(fù)雜的條件:
qs.Filter("profile__age__in", 18, 20).Exclude("profile__lt", 1000)
// WHERE profile.age IN (18, 20) AND NOT profile_id < 1000
可以通過如下接口獲取多條數(shù)據(jù),請看示例
例子1,根據(jù)條件age>17,獲取20位置開始的10條數(shù)據(jù)的數(shù)據(jù)
var allusers []User
qs.Filter("profile__age__gt", 17)
// WHERE profile.age > 17
例子2,limit默認(rèn)從10開始,獲取10條數(shù)據(jù)
qs.Limit(10, 20)
// LIMIT 10 OFFSET 20 注意跟SQL反過來的
beedb提供了豐富的刪除數(shù)據(jù)接口,請看下面的例子
例子1,刪除單條數(shù)據(jù)
o := orm.NewOrm()
if num, err := o.Delete(&User{Id: 1}); err == nil {
fmt.Println(num)
}
Delete 操作會對反向關(guān)系進(jìn)行操作,此例中 Post 擁有一個(gè)到 User 的外鍵。刪除 User 的時(shí)候。如果 on_delete 設(shè)置為默認(rèn)的級聯(lián)操作,將刪除對應(yīng)的 Post
有些應(yīng)用卻需要用到連接查詢,所以現(xiàn)在beego orm提供了一個(gè)簡陋的實(shí)現(xiàn)方案:
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)
上面代碼中我們看到了一個(gè)struct關(guān)聯(lián)查詢
針對有些應(yīng)用需要用到group by的功能,beego orm也提供了一個(gè)簡陋的實(shí)現(xiàn)
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
上面的代碼中出現(xiàn)了兩個(gè)新接口函數(shù)
GroupBy:用來指定進(jìn)行g(shù)roupby的字段
Having:用來指定having執(zhí)行的時(shí)候的條件
簡單示例:
o := orm.NewOrm()
var r orm.RawSeter
r = o.Raw("UPDATE user SET name = ? WHERE name = ?", "testing", "slene")
復(fù)雜原生sql使用:
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
}
更多說明,請到https://beego.vip/
目前beego orm已經(jīng)獲得了很多來自國內(nèi)外用戶的反饋,我目前也正在考慮支持更多數(shù)據(jù)庫,接下來會在更多方面進(jìn)行改進(jìn)
更多建議: