這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。
xorm
xorm是一個Go語言ORM庫. 通過它可以使資料庫操作非常簡便.
全部文檔點我
用法入門:
前提:定義本文中用到的struct和基本代碼如下
// 銀行賬戶type Account struct { Id int64 Name string `xorm:"unique"` Balance float64 Version int `xorm:"version"` // 樂觀鎖}var x *xorm.Engine
- 建立orm引擎
注意:若想配合mysql,需要提前載入mysql驅動,通過如此方式
import _ "github.com/go-sql-driver/mysql"
x,err:=xorm.NewEngine("mysql", "root:111111@/sys?charset=utf8")
- 自動同步表結構
if err = x.Sync2(new(Account)); err != nil { log.Fatalf("Fail to sync database: %v\n", err) }
Sync2會進行如下這些操作:
- 自動檢測和建立表,這個檢測是根據表的名字
- 自動檢測和新增表中的欄位,這個檢測是根據欄位名,同時對錶中多餘的欄位給出警告資訊
- 自動檢測,建立和刪除索引和唯一索引,這個檢測是根據索引的一個或多個欄位名,而不根據索引名稱。因此這裡需要注意,如果在一個有大量資料的表中引入新的索引,資料庫可能需要一定的時間來建立索引。
- 自動轉換varchar欄位類型到text欄位類型,自動警告其它欄位類型在模型和資料庫之間不一致的情況。
- 自動警告欄位的預設值,是否為空白資訊在模型和資料庫之間不匹配的情況
以上這些警告資訊需要將engine.ShowWarn
設定為 true
才會顯示。
- 增刪改操作
**增加操作:插入一條新的記錄,該記錄必須是未存在的,否則會返回錯誤:
**
_, err := x.Insert(&Account{Name: name, Balance: balance})
刪除操作:
_, err := x.Delete(&Account{Id: id})
方法 Delete 接受參數後,會自動根據傳進去的值進行尋找,然後刪除。比如此處,我們指定了 Account 的 ID 欄位,那麼就會刪除 ID 欄位值與我們所賦值相同的記錄;如果您只對 Name 欄位賦值,那麼 xorm 就會去尋找 Name 欄位值匹配的記錄。如果多個欄位同時賦值,則是多個條件同時滿足的記錄才會被刪除。
刪除操作針對的對象沒有限制,凡是按照條件尋找到的,都會被刪除(單個與大量刪除)。
擷取和修改記錄:想要修改的記錄必須是提前存在的,所以修改前要先查詢所要修改的記錄
擷取記錄:
Get方法
查詢單條資料使用Get方法,在調用Get方法時需要傳入一個對應結構體的指標,同時結構體中的非空field自動成為查詢的條件和前面的方法條件組合在一起查詢。
a. 根據Id來獲得單條資料:
a:=&Account{}has, err := x.Id(id).Get(a)
b. 根據where擷取單條資料
a := new(Account)has, err := x.Where("name=?", "adn").Get(a)
c. 根據Account結構體中存在的非空資料來擷取單條資料
a := &Account{Id:1}has, err := x.Get(a)
返回的結果為兩個參數,一個has(bool類型)為該條記錄是否存在,第二個參數err為是否有錯誤。不管err是否為nil,has都有可能為true或者false。
在擷取到記錄之後,我們就需要進行一些修改,然後更新到資料庫:
a.Balance += deposit// 對已有記錄進行更新_, err = x.Update(a)
注意,Update接受的參數是指標
批量擷取資訊
err = x.Desc("balance").Find(&as)
在這裡,我們還調用了 Desc 方法對記錄按照存款數額將賬戶從大到小排序。
Find方法的第一個參數為slice的指標或Map指標,即為查詢後返回的結果,第二個參數可選,為查詢的條件struct的指標。
- 樂觀鎖
樂觀鎖是 xorm 提供的一個比較實用的功能,通過在 tag 中指定 version 來開啟它。開啟之後,每次對記錄進行更新的時候,該欄位的值就會自動遞增 1。如此一來,您就可以判斷是否有其它地方同時修改了該記錄,如果是,則應當重新操作,否則會出現錯誤的資料(同時對一個帳號進行取款操作卻只扣了一次的數額)。
事務及復原
廢話不多說,直接上範例程式碼:
// 建立 Session 對象sess := x.NewSession()defer sess.Close()// 開啟事務if err = sess.Begin(); err != nil { return err}if _, err = sess.Update(a1); err != nil { // 發生錯誤時進行復原 sess.Rollback() return err} // 完成事務return sess.Commit()
統計記錄條數- Count方法
統計資料使用Count方法,Count方法的參數為struct的指標並且成為查詢條件。
a := new(Account)//返回滿足id>1的Account的記錄條數total, err := x.Where("id >?", 1).Count(a)//返回Account所有記錄條數total,err = x.Count(a)
Iterate方法
Iterate方法提供逐條執行查詢到的記錄的方法,他所能使用的條件和Find方法完全相同
err := x.Where("id > ?=)", 30).Iterate(new(Account), func(i int, bean interface{})error{ user := bean.(*Account) //do somthing use i and user})
查詢特定欄位
我們主要來看迭代函數的聲明:它接受 2 個參數,第一個是目前記錄所對應的索引(該索引和 ID 的值毫無關係,只是查詢後結果的索引),第二個參數則是儲存了相互關聯類型的空介面,需要自行斷言,例如樣本中使用 bean.(*Account) 因為我們知道查詢的結構是 Account。
查詢特定欄位
使用 Cols 方法可以指定查詢特定欄位,當只有結構中的某個欄位的值對您有價值時,就可以使用它:
x.Cols("name").Iterate(new(Account), printFn)var printFn = func(idx int, bean interface{}) error { //dosomething return nil}
此處,所查詢出來的結構只有 Name 欄位有值,其它欄位均為零值。要注意的是,Cols 方法所接受的參數是資料表中對應的名稱,而不是欄位名稱。
排除特定欄位
當您希望刻意忽略某個欄位的查詢結果時,可以使用 Omit 方法:
x.Omit("name").Iterate(new(Account), printFn)
此處,所查詢出來的結構只有 Name 欄位為零值。要注意的是,Omit 方法所接受的參數是資料表中對應的名稱,而不是欄位名稱。
查詢結果位移
查詢結果位移在分頁應用中最為常見,通過 Limit 方法可以達到一樣的目的:
x.Limit(3, 2).Iterate(new(Account), printFn)
該方法最少接受 1 個參數,第一個參數表示取出的最大記錄數;如果傳入第二個參數,則表示對查詢結果進行位移。因此,此處的查詢結果為位移 2 個後,再最多取出 3 個記錄。
日誌記錄
一般情況下,使用x.ShowSQL = true
來開啟 xorm 最基本的日誌功能,所有 SQL 都會被列印到控制台,但如果您想要將日誌儲存到檔案,則可以在擷取到 ORM 引擎之後,進行如下操作:
f, err := os.Create("sql.log")if err != nil { log.Fatalf("Fail to create log file: %v\n", err) return}x.Logger = xorm.NewSimpleLogger(f)
LRU 緩衝
作為唯一支援 LRU 緩衝的一款 ORM,如果不知道如何使用這個特性,那將是非常遺憾。不過,想要使用它也並不困難,只需要在擷取到 ORM 引擎之後,進行如下操作:
cacher := xorm.NewLRUCacher(xorm.NewMemoryStore(), 1000)x.SetDefaultCacher(cacher)
這樣就算是使用最基本的緩衝功能了。該功能還支援只緩衝某些表或排除緩衝某些表,詳情可以參見 文章首部的官方文檔。
事件鉤子
官方一共提供了 6 類 事件鉤子,樣本中只示範其中 2 種:BeforeInsert 和 AfterInsert。全部內容查看文章首部官方文檔
它們的作用分別會在 進行插入記錄之前 和 完成插入記錄之後 被調用:
func (a *Account) BeforeInsert() {
log.Printf("before insert: %s", a.Name)
}
func (a *Account) AfterInsert() {
log.Printf("after insert: %s", a.Name)
}
下面是一個簡單的銀行存取款的小例子
package mainimport ( "errors" "log" "github.com/go-xorm/xorm" _ "github.com/mattn/go-sqlite3")// 銀行賬戶type Account struct { Id int64 Name string `xorm:"unique"` Balance float64 Version int `xorm:"version"` // 樂觀鎖}// ORM 引擎var x *xorm.Enginefunc init() { // 建立 ORM 引擎與資料庫 var err error x, err = xorm.NewEngine("mysql", "root:111111@/sys?charset=utf8") if err != nil { log.Fatalf("Fail to create engine: %v\n", err) } // 同步結構體與資料表 if err = x.Sync(new(Account)); err != nil { log.Fatalf("Fail to sync database: %v\n", err) }}// 建立新的賬戶func newAccount(name string, balance float64) error { // 對未存在記錄進行插入 _, err := x.Insert(&Account{Name: name, Balance: balance}) return err}// 擷取賬戶資訊func getAccount(id int64) (*Account, error) { a := &Account{} // 直接操作 ID 的簡便方法 has, err := x.Id(id).Get(a) // 判斷操作是否發生錯誤或對象是否存在 if err != nil { return nil, err } else if !has { return nil, errors.New("Account does not exist") } return a, nil}// 使用者轉賬func makeTransfer(id1, id2 int64, balance float64) error { // 建立 Session 對象 sess := x.NewSession() defer sess.Close() // 啟動事務 if err = sess.Begin(); err != nil { return err } a1, err := getAccount(id1) if err != nil { return err } a2, err := getAccount(id2) if err != nil { return err } if a1.Balance < balance { return errors.New("Not enough balance") } a1.Balance -= balance a2.Balance += balance if _, err = sess.Update(a1); err != nil { // 發生錯誤時進行復原 sess.Rollback() return err } if _, err = sess.Update(a2); err != nil { sess.Rollback() return err } // 完成事務 return sess.Commit() return nil}// 使用者存款func makeDeposit(id int64, deposit float64) (*Account, error) { a, err := getAccount(id) if err != nil { return nil, err } sess := x.NewSession() defer sess.Close() if err = sess.Begin(); err != nil { return nil, err } a.Balance += deposit // 對已有記錄進行更新 if _, err = sess.Update(a); err != nil { sess.Rollback() return nil, err } return a, sess.Commit()}// 使用者取款func makeWithdraw(id int64, withdraw float64) (*Account, error) { a, err := getAccount(id) if err != nil { return nil, err } if a.Balance < withdraw { return nil, errors.New("Not enough balance") } sess := x.NewSession() defer sess.Close() if _, err = sess.Begin(); err != nil { return nil, err } a.Balance -= withdraw if _, err = sess.Update(a); err != nil { return nil, err } return a, sess.Commit()}// 按照 ID 正序排序返回所有賬戶func getAccountsAscId() (as []Account, err error) { // 使用 Find 方法批量擷取記錄 err = x.Find(&as) return as, err}// 按照存款倒序排序返回所有賬戶func getAccountsDescBalance() (as []Account, err error) { // 使用 Desc 方法使結果呈倒序排序 err = x.Desc("balance").Find(&as) return as, err}// 刪除賬戶func deleteAccount(id int64) error { // 通過 Delete 方法刪除記錄 _, err := x.Delete(&Account{Id: id}) return err}
註:本文參考
- Go名庫講解
- 官方文檔