golang-xorm庫快速學(xué)習(xí)

xorm

xorm是一個(gè)Go語言O(shè)RM庫. 通過它可以使數(shù)據(jù)庫操作非常簡便.

全部文檔點(diǎn)我

用法入門:

前提:定義本文中用到的struct和基本代碼如下

// 銀行賬戶
type Account struct {
    Id      int64
    Name    string `xorm:"unique"`
    Balance float64
    Version int `xorm:"version"` // 樂觀鎖
}
var x *xorm.Engine
  1. 創(chuàng)建orm引擎

注意:若想配合mysql福扬,需要提前加載mysql驅(qū)動(dòng)腕铸,通過如此方式

import _ "github.com/go-sql-driver/mysql"

x,err:=xorm.NewEngine("mysql", "root:111111@/sys?charset=utf8")

  1. 自動(dòng)同步表結(jié)構(gòu)
if err = x.Sync2(new(Account)); err != nil {
        log.Fatalf("Fail to sync database: %v\n", err)
    }

Sync2會(huì)進(jìn)行如下這些操作:

  • 自動(dòng)檢測和創(chuàng)建表,這個(gè)檢測是根據(jù)表的名字
  • 自動(dòng)檢測和新增表中的字段铛碑,這個(gè)檢測是根據(jù)字段名狠裹,同時(shí)對表中多余的字段給出警告信息
  • 自動(dòng)檢測,創(chuàng)建和刪除索引和唯一索引汽烦,這個(gè)檢測是根據(jù)索引的一個(gè)或多個(gè)字段名涛菠,而不根據(jù)索引名稱。因此這里需要注意撇吞,如果在一個(gè)有大量數(shù)據(jù)的表中引入新的索引俗冻,數(shù)據(jù)庫可能需要一定的時(shí)間來建立索引。
  • 自動(dòng)轉(zhuǎn)換varchar字段類型到text字段類型梢夯,自動(dòng)警告其它字段類型在模型和數(shù)據(jù)庫之間不一致的情況言疗。
  • 自動(dòng)警告字段的默認(rèn)值,是否為空信息在模型和數(shù)據(jù)庫之間不匹配的情況

以上這些警告信息需要將engine.ShowWarn 設(shè)置為 true 才會(huì)顯示颂砸。

  1. 增刪改操作

**增加操作:插入一條新的記錄噪奄,該記錄必須是未存在的死姚,否則會(huì)返回錯(cuò)誤:
**
_, err := x.Insert(&Account{Name: name, Balance: balance})

刪除操作:

_, err := x.Delete(&Account{Id: id})

方法 Delete 接受參數(shù)后,會(huì)自動(dòng)根據(jù)傳進(jìn)去的值進(jìn)行查找勤篮,然后刪除都毒。比如此處,我們指定了 Account 的 ID 字段碰缔,那么就會(huì)刪除 ID 字段值與我們所賦值相同的記錄账劲;如果您只對 Name 字段賦值,那么 xorm 就會(huì)去查找 Name 字段值匹配的記錄金抡。如果多個(gè)字段同時(shí)賦值瀑焦,則是多個(gè)條件同時(shí)滿足的記錄才會(huì)被刪除。

刪除操作針對的對象沒有限制梗肝,凡是按照條件查找到的榛瓮,都會(huì)被刪除(單個(gè)與批量刪除)。

獲取和修改記錄:想要修改的記錄必須是提前存在的巫击,所以修改前要先查詢所要修改的記錄

獲取記錄:

Get方法

查詢單條數(shù)據(jù)使用Get方法禀晓,在調(diào)用Get方法時(shí)需要傳入一個(gè)對應(yīng)結(jié)構(gòu)體的指針,同時(shí)結(jié)構(gòu)體中的非空field自動(dòng)成為查詢的條件和前面的方法條件組合在一起查詢坝锰。

a. 根據(jù)Id來獲得單條數(shù)據(jù):

a:=&Account{}
has, err := x.Id(id).Get(a)

b. 根據(jù)where獲取單條數(shù)據(jù)

a := new(Account)
has, err := x.Where("name=?", "adn").Get(a)

c. 根據(jù)Account結(jié)構(gòu)體中存在的非空數(shù)據(jù)來獲取單條數(shù)據(jù)

a := &Account{Id:1}
has, err := x.Get(a)

返回的結(jié)果為兩個(gè)參數(shù)粹懒,一個(gè)has(bool類型)為該條記錄是否存在,第二個(gè)參數(shù)err為是否有錯(cuò)誤顷级。不管err是否為nil凫乖,has都有可能為true或者false。

在獲取到記錄之后愕把,我們就需要進(jìn)行一些修改拣凹,然后更新到數(shù)據(jù)庫:

a.Balance += deposit
// 對已有記錄進(jìn)行更新
_, err = x.Update(a)

注意,Update接受的參數(shù)是指針

批量獲取信息

err = x.Desc("balance").Find(&as)

在這里恨豁,我們還調(diào)用了 Desc 方法對記錄按照存款數(shù)額將賬戶從大到小排序嚣镜。

Find方法的第一個(gè)參數(shù)為slice的指針或Map指針,即為查詢后返回的結(jié)果橘蜜,第二個(gè)參數(shù)可選菊匿,為查詢的條件struct的指針。

  1. 樂觀鎖

樂觀鎖是 xorm 提供的一個(gè)比較實(shí)用的功能计福,通過在 tag 中指定 version 來開啟它跌捆。開啟之后,每次對記錄進(jìn)行更新的時(shí)候象颖,該字段的值就會(huì)自動(dòng)遞增 1佩厚。如此一來,您就可以判斷是否有其它地方同時(shí)修改了該記錄说订,如果是抄瓦,則應(yīng)當(dāng)重新操作潮瓶,否則會(huì)出現(xiàn)錯(cuò)誤的數(shù)據(jù)(同時(shí)對一個(gè)帳號進(jìn)行取款操作卻只扣了一次的數(shù)額)。

事務(wù)及回滾

廢話不多說钙姊,直接上示例代碼:

// 創(chuàng)建 Session 對象
sess := x.NewSession()
defer sess.Close()
// 開啟事務(wù)
if err = sess.Begin(); err != nil {
    return err
}

if _, err = sess.Update(a1); err != nil {
    // 發(fā)生錯(cuò)誤時(shí)進(jìn)行回滾
    sess.Rollback()
    return err
} 

// 完成事務(wù)
return sess.Commit()

統(tǒng)計(jì)記錄條數(shù)- Count方法

統(tǒng)計(jì)數(shù)據(jù)使用Count方法毯辅,Count方法的參數(shù)為struct的指針并且成為查詢條件。


a := new(Account)
//返回滿足id>1的Account的記錄條數(shù)
total, err := x.Where("id >?", 1).Count(a)
//返回Account所有記錄條數(shù)
total,err = x.Count(a)

Iterate方法

Iterate方法提供逐條執(zhí)行查詢到的記錄的方法煞额,他所能使用的條件和Find方法完全相同

err := x.Where("id > ?=)", 30).Iterate(new(Account), func(i int, bean interface{})error{
    user := bean.(*Account)
    //do somthing use i and user
})

我們主要來看迭代函數(shù)的聲明:它接受 2 個(gè)參數(shù)思恐,第一個(gè)是當(dāng)前記錄所對應(yīng)的索引(該索引和 ID 的值毫無關(guān)系,只是查詢后結(jié)果的索引)膊毁,第二個(gè)參數(shù)則是保存了相關(guān)類型的空接口胀莹,需要自行斷言,例如示例中使用 bean.(*Account) 因?yàn)槲覀冎啦樵兊慕Y(jié)構(gòu)是 Account婚温。

查詢特定字段

使用 Cols 方法可以指定查詢特定字段嗜逻,當(dāng)只有結(jié)構(gòu)中的某個(gè)字段的值對您有價(jià)值時(shí),就可以使用它:

x.Cols("name").Iterate(new(Account), printFn)

var printFn = func(idx int, bean interface{}) error {
    //dosomething
    return nil
}

此處缭召,所查詢出來的結(jié)構(gòu)只有 Name 字段有值,其它字段均為零值逆日。要注意的是嵌巷,Cols 方法所接受的參數(shù)是數(shù)據(jù)表中對應(yīng)的名稱,而不是字段名稱室抽。

排除特定字段

當(dāng)您希望刻意忽略某個(gè)字段的查詢結(jié)果時(shí)搪哪,可以使用 Omit 方法:

x.Omit("name").Iterate(new(Account), printFn)
此處,所查詢出來的結(jié)構(gòu)只有 Name 字段為零值坪圾。要注意的是晓折,Omit 方法所接受的參數(shù)是數(shù)據(jù)表中對應(yīng)的名稱,而不是字段名稱兽泄。

查詢結(jié)果偏移

查詢結(jié)果偏移在分頁應(yīng)用中最為常見漓概,通過 Limit 方法可以達(dá)到一樣的目的:

x.Limit(3, 2).Iterate(new(Account), printFn)

該方法最少接受 1 個(gè)參數(shù),第一個(gè)參數(shù)表示取出的最大記錄數(shù)病梢;如果傳入第二個(gè)參數(shù)胃珍,則表示對查詢結(jié)果進(jìn)行偏移。因此蜓陌,此處的查詢結(jié)果為偏移 2 個(gè)后觅彰,再最多取出 3 個(gè)記錄。

日志記錄

一般情況下钮热,使用x.ShowSQL = true來開啟 xorm 最基本的日志功能填抬,所有 SQL 都會(huì)被打印到控制臺(tái),但如果您想要將日志保存到文件隧期,則可以在獲取到 ORM 引擎之后飒责,進(jìn)行如下操作:

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赘娄,如果不知道如何使用這個(gè)特性,那將是非常遺憾读拆。不過擅憔,想要使用它也并不困難,只需要在獲取到 ORM 引擎之后檐晕,進(jìn)行如下操作:

cacher := xorm.NewLRUCacher(xorm.NewMemoryStore(), 1000)
x.SetDefaultCacher(cacher)

這樣就算是使用最基本的緩存功能了暑诸。該功能還支持只緩存某些表或排除緩存某些表,詳情可以參見 文章首部的官方文檔辟灰。

事件鉤子

官方一共提供了 6 類 事件鉤子个榕,示例中只演示其中 2 種:BeforeInsert 和 AfterInsert。全部內(nèi)容查看文章首部官方文檔

它們的作用分別會(huì)在 進(jìn)行插入記錄之前 和 完成插入記錄之后 被調(diào)用:

func (a *Account) BeforeInsert() {
log.Printf("before insert: %s", a.Name)
}

func (a *Account) AfterInsert() {
log.Printf("after insert: %s", a.Name)
}

下面是一個(gè)簡單的銀行存取款的小例子

package main

import (
    "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.Engine

func init() {
    // 創(chuàng)建 ORM 引擎與數(shù)據(jù)庫
    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)
    }

    // 同步結(jié)構(gòu)體與數(shù)據(jù)表
    if err = x.Sync(new(Account)); err != nil {
        log.Fatalf("Fail to sync database: %v\n", err)
    }
}

// 創(chuàng)建新的賬戶
func newAccount(name string, balance float64) error {
    // 對未存在記錄進(jìn)行插入
    _, 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)
    // 判斷操作是否發(fā)生錯(cuò)誤或?qū)ο笫欠翊嬖?    if err != nil {
        return nil, err
    } else if !has {
        return nil, errors.New("Account does not exist")
    }
    return a, nil
}

// 用戶轉(zhuǎn)賬
func makeTransfer(id1, id2 int64, balance float64) error {
    // 創(chuàng)建 Session 對象
    sess := x.NewSession()
    defer sess.Close()
    // 啟動(dòng)事務(wù)
    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 {
        // 發(fā)生錯(cuò)誤時(shí)進(jìn)行回滾
        sess.Rollback()
        return err
    }
    if _, err = sess.Update(a2); err != nil {
        sess.Rollback()
        return err
    }
    // 完成事務(wù)
    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
    // 對已有記錄進(jìn)行更新
    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 方法使結(jié)果呈倒序排序
    err = x.Desc("balance").Find(&as)
    return as, err
}

// 刪除賬戶
func deleteAccount(id int64) error {
    // 通過 Delete 方法刪除記錄
    _, err := x.Delete(&Account{Id: id})
    return err
}

注:本文參考

  1. Go名庫講解
  2. 官方文檔
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末芥喇,一起剝皮案震驚了整個(gè)濱河市西采,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌继控,老刑警劉巖械馆,帶你破解...
    沈念sama閱讀 221,198評論 6 514
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異武通,居然都是意外死亡霹崎,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,334評論 3 398
  • 文/潘曉璐 我一進(jìn)店門冶忱,熙熙樓的掌柜王于貴愁眉苦臉地迎上來尾菇,“玉大人,你說我怎么就攤上這事囚枪∨晌埽” “怎么了?”我有些...
    開封第一講書人閱讀 167,643評論 0 360
  • 文/不壞的土叔 我叫張陵链沼,是天一觀的道長默赂。 經(jīng)常有香客問我,道長括勺,這世上最難降的妖魔是什么放可? 我笑而不...
    開封第一講書人閱讀 59,495評論 1 296
  • 正文 為了忘掉前任,我火速辦了婚禮朝刊,結(jié)果婚禮上耀里,老公的妹妹穿的比我還像新娘。我一直安慰自己拾氓,他們只是感情好冯挎,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,502評論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般房官。 火紅的嫁衣襯著肌膚如雪趾徽。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,156評論 1 308
  • 那天翰守,我揣著相機(jī)與錄音孵奶,去河邊找鬼。 笑死蜡峰,一個(gè)胖子當(dāng)著我的面吹牛了袁,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播湿颅,決...
    沈念sama閱讀 40,743評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼载绿,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了油航?” 一聲冷哼從身側(cè)響起崭庸,我...
    開封第一講書人閱讀 39,659評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎谊囚,沒想到半個(gè)月后怕享,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,200評論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡镰踏,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,282評論 3 340
  • 正文 我和宋清朗相戀三年熬粗,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片余境。...
    茶點(diǎn)故事閱讀 40,424評論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖灌诅,靈堂內(nèi)的尸體忽然破棺而出芳来,到底是詐尸還是另有隱情,我是刑警寧澤猜拾,帶...
    沈念sama閱讀 36,107評論 5 349
  • 正文 年R本政府宣布即舌,位于F島的核電站,受9級特大地震影響挎袜,放射性物質(zhì)發(fā)生泄漏顽聂。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,789評論 3 333
  • 文/蒙蒙 一盯仪、第九天 我趴在偏房一處隱蔽的房頂上張望紊搪。 院中可真熱鬧,春花似錦全景、人聲如沸耀石。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,264評論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽滞伟。三九已至揭鳞,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間梆奈,已是汗流浹背野崇。 一陣腳步聲響...
    開封第一講書人閱讀 33,390評論 1 271
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留亩钟,地道東北人乓梨。 一個(gè)月前我還...
    沈念sama閱讀 48,798評論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像径荔,于是被迫代替她去往敵國和親督禽。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,435評論 2 359

推薦閱讀更多精彩內(nèi)容