模型(Models)- beego ORM
原文
beego ORM 是一個強大的 Go 語言 ORM 架構。她的靈感主要來自 Django ORM 和 SQLAlchemy。
目前該架構仍處於開發階段,可能發生任何導致不相容的改動。
已支援資料庫驅動:
- MySQL:github.com/go-sql-driver/mysql
- PostgreSQL:github.com/lib/pq
- Sqlite3:github.com/mattn/go-sqlite3
以上資料庫驅動均通過基本測試,但我們仍需要您的反饋。
ORM 特性:
- 支援 Go 的所有類型儲存
- 輕鬆上手,採用簡單的 CRUD 風格
- 自動 Join 關聯表
- 跨資料庫相容查詢
- 允許直接使用 SQL 查詢/映射
- 嚴格完整的測試保證 ORM 的穩定與健壯
安裝 ORM:
go get github.com/astaxie/beego/orm
ORM 使用方法
比較規範的,現在models的檔案夾下建立model的對象,所有的關於資料庫的操作都是在models中進行的,controller調用models中的方法即可。
models.go:
package mainimport ( "github.com/astaxie/beego/orm" //匯入包)//定義 struct和資料表進行映射的type User struct { 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)"`}func init() { // 需要在init中註冊定義的model orm.RegisterModel(new(User), new(Post), new(Profile), new(Tag))}
main.go
package mainimport ( "fmt" "github.com/astaxie/beego/orm" _ "github.com/go-sql-driver/mysql" // 只是調用init方法)func init() { orm.RegisterDriver("mysql", orm.DRMySQL) orm.RegisterDataBase("default", "mysql", "root:root@/orm_test?charset=utf8")}func main() { o := orm.NewOrm() o.Using("default") // 預設使用 default,你可以指定為其他資料庫 profile := new(Profile) profile.Age = 30 user := new(User) user.Profile = profile user.Name = "slene" fmt.Println(o.Insert(profile)) fmt.Println(o.Insert(user))}
資料庫的設定
目前 ORM 支援三種資料庫,以下為測試過的 driver
將你需要使用的 driver 加入 import 中
import ( _ "github.com/go-sql-driver/mysql" _ "github.com/lib/pq" _ "github.com/mattn/go-sqlite3")
RegisterDriver
三種預設資料庫類型
// For version 1.6orm.DRMySQLorm.DRSqliteorm.DRPostgres// < 1.6orm.DR_MySQLorm.DR_Sqliteorm.DR_Postgres
// 參數1 driverName// 參數2 資料庫類型// 這個用來設定 driverName 對應的資料庫類型// mysql / sqlite3 / postgres 這三種是預設已經註冊過的,所以可以無需設定orm.RegisterDriver("mysql", orm.DRMySQL)
RegisterDataBase
ORM 必須註冊一個別名為 default
的資料庫,作為預設使用。
ORM 使用 golang 自己的串連池
// RegisterDataBase Setting the database connect params. Use the database driver self dataSource args.func RegisterDataBase(aliasName, driverName, dataSource string, params ...int) error { var ( err error db *sql.DB al *alias ) db, err = sql.Open(driverName, dataSource) if err != nil { err = fmt.Errorf("register db `%s`, %s", aliasName, err.Error()) goto end } al, err = addAliasWthDB(aliasName, driverName, db) if err != nil { goto end } al.DataSource = dataSource detectTZ(al) for i, v := range params { switch i { case 0: SetMaxIdleConns(al.Name, v) case 1: SetMaxOpenConns(al.Name, v) } }end: if err != nil { if db != nil { db.Close() } DebugLog.Println(err.Error()) } return err}
// 參數1 資料庫的別名,用來在 ORM 中切換資料庫使用// 參數2 driverName// 參數3 對應的連結字串orm.RegisterDataBase("default", "mysql", "root:root@/orm_test?charset=utf8")// 參數4(可選) 設定最大空閑串連// 參數5(可選) 設定最大資料庫連接 (go >= 1.2)maxIdle := 30maxConn := 30orm.RegisterDataBase("default", "mysql", "root:root@/orm_test?charset=utf8", maxIdle, maxConn)
SetMaxIdleConns
根據資料庫的別名,設定資料庫的最大空閑串連
orm.SetMaxIdleConns("default", 30)
SetMaxOpenConns
根據資料庫的別名,設定資料庫的最大資料庫連接 (go >= 1.2)
orm.SetMaxOpenConns("default", 30)
時區設定
ORM 預設使用 time.Local 本地時區
- 作用於 ORM 自動建立的時間
- 從資料庫中取回的時間轉換成 ORM 本地時間
如果需要的話,你也可以變更
// 設定為 UTC 時間orm.DefaultTimeLoc = time.UTC
ORM 在進行 RegisterDataBase 的同時,會擷取資料庫使用的時區,然後在 time.Time 類型存取時做相應轉換,以匹配時間系統,從而保證時間不會出錯。
注意:
- 鑒於 Sqlite3 的設計,存取預設都為 UTC 時間
- 使用 go-sql-driver 驅動時,請注意參數設定
從某一版本開始,驅動預設使用 UTC 時間,而非本地時間,所以請指定時區參數或者全部以 UTC 時間存取
例如:root:root@/orm_test?charset=utf8&loc=Asia%2FShanghai
參見 loc / parseTime
註冊模型
如果使用 orm.QuerySeter 進行進階查詢的話,這個是必須的。
反之,如果只使用 Raw 查詢和 map struct,是無需這一步的。您可以去查看 Raw SQL 查詢
RegisterModel
將你定義的 Model 進行註冊,最佳設計是有單獨的 models.go 檔案,在他的 init 函數中進行註冊。
迷你版 models.go
package mainimport "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 定義請查看文檔 模型定義
RegisterModelWithPrefix
使用表名首碼
orm.RegisterModelWithPrefix("prefix_", new(User))
建立後的表名為 prefix_user
NewOrmWithDB
有時候需要自行管理串連池與資料庫連結(比如:go 的連結池無法讓兩次查詢使用同一個連結的)
但又想使用 ORM 的查詢功能
var driverName, aliasName string// driverName 是驅動的名稱// aliasName 是當前 db 的自訂別名var db *sql.DB...o := orm.NewOrmWithDB(driverName, aliasName, db)
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")}
ResetModelCache
重設已經註冊的模型 struct,一般用於編寫測試案例
orm.ResetModelCache()
ORM 介面使用
使用 ORM 必然接觸的 Ormer 介面,我們來熟悉一下
var o Ormero = orm.NewOrm() // 建立一個 Ormer// NewOrm 的同時會執行 orm.BootStrap (整個 app 只執行一次),用以驗證模型之間的定義並緩衝。
切換資料庫,或者,進行交易處理,都會作用於這個 Ormer 對象,以及其進行的任何查詢。
所以:需要 切換資料庫 和 交易處理 的話,不要使用全域儲存的 Ormer 對象。
// Ormer define the orm interfacetype Ormer interface { // read data to model // for example: // this will find User by Id field // u = &User{Id: user.Id} // err = Ormer.Read(u) // this will find User by UserName field // u = &User{UserName: "astaxie", Password: "pass"} // err = Ormer.Read(u, "UserName") Read(md interface{}, cols ...string) error // Like Read(), but with "FOR UPDATE" clause, useful in transaction. // Some databases are not support this feature. ReadForUpdate(md interface{}, cols ...string) error // Try to read a row from the database, or insert one if it doesn't exist ReadOrCreate(md interface{}, col1 string, cols ...string) (bool, int64, error) // insert model data to database // for example: // user := new(User) // id, err = Ormer.Insert(user) // user must a pointer and Insert will set user's pk field Insert(interface{}) (int64, error) // mysql:InsertOrUpdate(model) or InsertOrUpdate(model,"colu=colu+value") // if colu type is integer : can use(+-*/), string : convert(colu,"value") // postgres: InsertOrUpdate(model,"conflictColumnName") or InsertOrUpdate(model,"conflictColumnName","colu=colu+value") // if colu type is integer : can use(+-*/), string : colu || "value" InsertOrUpdate(md interface{}, colConflitAndArgs ...string) (int64, error) // insert some models to database InsertMulti(bulk int, mds interface{}) (int64, error) // update model to database. // cols set the columns those want to update. // find model by Id(pk) field and update columns specified by fields, if cols is null then update all columns // for example: // user := User{Id: 2} // user.Langs = append(user.Langs, "zh-CN", "en-US") // user.Extra.Name = "beego" // user.Extra.Data = "orm" // num, err = Ormer.Update(&user, "Langs", "Extra") Update(md interface{}, cols ...string) (int64, error) // delete model in database Delete(md interface{}, cols ...string) (int64, error) // load related models to md model. // args are limit, offset int and order string. // // example: // Ormer.LoadRelated(post,"Tags") // for _,tag := range post.Tags{...} //args[0] bool true useDefaultRelsDepth ; false depth 0 //args[0] int loadRelationDepth //args[1] int limit default limit 1000 //args[2] int offset default offset 0 //args[3] string order for example : "-Id" // make sure the relation is defined in model struct tags. LoadRelated(md interface{}, name string, args ...interface{}) (int64, error) // create a models to models queryer // for example: // post := Post{Id: 4} // m2m := Ormer.QueryM2M(&post, "Tags") QueryM2M(md interface{}, name string) QueryM2Mer // return a QuerySeter for table operations. // table name can be string or struct. // e.g. QueryTable("user"), QueryTable(&user{}) or QueryTable((*User)(nil)), QueryTable(ptrStructOrTableName interface{}) QuerySeter // switch to another registered database driver by given name. Using(name string) error // begin transaction // for example: // o := NewOrm() // err := o.Begin() // ... // err = o.Rollback() Begin() error // commit transaction Commit() error // rollback transaction Rollback() error // return a raw query seter for raw sql string. // for example: // ormer.Raw("UPDATE `user` SET `user_name` = ? WHERE `user_name` = ?", "slene", "testing").Exec() // // update user testing's name to slene Raw(query string, args ...interface{}) RawSeter Driver() Driver}
QueryTable
傳入表名,或者 Model 對象,返回一個 QuerySeter
o := orm.NewOrm()var qs QuerySeterqs = o.QueryTable("user")// 如果表沒有定義過,會立刻 panic
Using
切換為其他資料庫
orm.RegisterDataBase("db1", "mysql", "root:root@/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
Raw
使用 sql 語句直接進行操作
Raw 函數,返回一個 RawSeter 用以對設定的 sql 語句和參數進行操作
o := NewOrm()var r RawSeterr = o.Raw("UPDATE user SET name = ? WHERE name = ?", "testing", "slene")
Driver
返回當前 ORM 使用的 db 資訊
type Driver interface { Name() string Type() DriverType}
orm.RegisterDataBase("db1", "mysql", "root:root@/orm_db2?charset=utf8")orm.RegisterDataBase("db2", "sqlite3", "data.db")o1 := orm.NewOrm()o1.Using("db1")dr := o1.Driver()fmt.Println(dr.Name() == "db1") // truefmt.Println(dr.Type() == orm.DRMySQL) // trueo2 := orm.NewOrm()o2.Using("db2")dr = o2.Driver()fmt.Println(dr.Name() == "db2") // truefmt.Println(dr.Type() == orm.DRSqlite) // true
偵錯模式列印查詢語句
簡單的設定 Debug 為 true 列印查詢的語句
可能存在效能問題,不建議使用在生產模式
func main() { orm.Debug = true...
預設使用 os.Stderr 輸出日誌資訊
改變輸出到你自己的 io.Writer
var w io.Writer...// 設定為你的 io.Writer...orm.DebugLog = orm.NewLog(w)