go get如何删除_Go 每日一库之 xorm
簡介
Go 標(biāo)準(zhǔn)庫提供的數(shù)據(jù)庫接口database/sql比較底層,使用它來操作數(shù)據(jù)庫非常繁瑣,而且容易出錯。因而社區(qū)開源了不少第三方庫,如上一篇文章中的sqlc工具,還有各式各樣的 ORM (Object Relational Mapping,對象關(guān)系映射庫),如gorm和xorm。本文介紹xorm。xorm是一個簡單但強大的 Go 語言 ORM 庫,使用它可以大大簡化我們的數(shù)據(jù)庫操作。
快速使用
先安裝:
$?go?get?xorm.io/xorm由于需要操作具體的數(shù)據(jù)庫(本文中我們使用 MySQL),需要安裝對應(yīng)的驅(qū)動:
$?go?get?github.com/go-sql-driver/mysql使用:
package?mainimport?(
??"log"
??"time"
??_?"github.com/go-sql-driver/mysql"
??"xorm.io/xorm"
)
type?User?struct?{
??Id??????int64
??Name????string
??Salt????string
??Age?????int
??Passwd??string????`xorm:"varchar(200)"`
??Created?time.Time?`xorm:"created"`
??Updated?time.Time?`xorm:"updated"`
}
func?main()?{
??engine,?err?:=?xorm.NewEngine("mysql",?"root:12345@/test?charset=utf8")
??if?err?!=?nil?{
????log.Fatal(err)
??}
??err?=?engine.Sync2(new(User))
??if?err?!=?nil?{
????log.Fatal(err)
??}
}
使用xorm來操作數(shù)據(jù)庫,首先需要使用xorm.NewEngine()創(chuàng)建一個引擎。該方法的參數(shù)與sql.Open()參數(shù)相同。
上面代碼中,我們演示了xorm的一個非常實用的功能,將數(shù)據(jù)庫中的表與對應(yīng) Go 代碼中的結(jié)構(gòu)體做同步。初始狀態(tài)下,數(shù)據(jù)庫test中沒有表user,調(diào)用Sync2()方法會根據(jù)User的結(jié)構(gòu)自動創(chuàng)建一個user表。執(zhí)行后,通過describe user查看表結(jié)構(gòu):
如果表user已經(jīng)存在,Sync()方法會對比User結(jié)構(gòu)與表結(jié)構(gòu)的不同,對表做相應(yīng)的修改。我們給User結(jié)構(gòu)添加一個Level字段:
type?User?struct?{??Id??????int64
??Name????string
??Salt????string
??Age?????int
??Level???int
??Passwd??string????`xorm:"varchar(200)"`
??Created?time.Time?`xorm:"created"`
??Updated?time.Time?`xorm:"updated"`
}
再次執(zhí)行這個程序后,用describe user命令查看表結(jié)構(gòu):
發(fā)現(xiàn)表中多了一個level字段。
**此修改只限于添加字段。**刪除表中已有的字段會帶來比較大的風(fēng)險。如果我們User結(jié)構(gòu)的Salt字段刪除,然后執(zhí)行程序。出現(xiàn)下面錯誤:
[xorm]?[warn]??2020/05/07?22:44:38.528784?Table?user?has?column?salt?but?struct?has?not?related?field數(shù)據(jù)庫操作
查詢&統(tǒng)計
xorm提供了幾個查詢和統(tǒng)計方法,Get/Exist/Find/Iterate/Count/Rows/Sum。下面逐一介紹。
為了代碼演示方便,我在user表中插入了一些數(shù)據(jù):
后面的代碼為了簡單起見,忽略了錯誤處理,實際使用中不要漏掉!
Get
Get()方法用于查詢單條數(shù)據(jù),并使用返回的字段為傳入的對象賦值:
type?User?struct?{??Id??????int64
??Name????string
??Salt????string
??Age?????int
??Passwd??string????`xorm:"varchar(200)"`
??Created?time.Time?`xorm:"created"`
??Updated?time.Time?`xorm:"updated"`
}
func?main()?{
??engine,?_?:=?xorm.NewEngine("mysql",?"root:12345@/test?charset=utf8")
??user1?:=?&User{}
??has,?_?:=?engine.ID(1).Get(user1)
??if?has?{
????fmt.Printf("user1:%v\n",?user1)
??}
??user2?:=?&User{}
??has,?_?=?engine.Where("name=?",?"dj").Get(user2)
??if?has?{
????fmt.Printf("user2:%v\n",?user2)
??}
??user3?:=?&User{Id:?5}
??has,?_?=?engine.Get(user3)
??if?has?{
????fmt.Printf("user3:%v\n",?user3)
??}
??user4?:=?&User{Name:?"pipi"}
??has,?_?=?engine.Get(user4)
??if?has?{
????fmt.Printf("user4:%v\n",?user4)
??}
}
上面演示了 3 種使用Get()的方式:
- 使用主鍵:engine.ID(1)查詢主鍵(即id)為 1 的用戶;
- 使用條件語句:engine.Where("name=?", "dj")查詢name = "dj"的用戶;
- 使用對象中的非空字段:user3設(shè)置了Id字段為 5,engine.Get(user3)查詢id = 5的用戶;user4設(shè)置了字段Name為"pipi",engine.Get(user4)查詢name = "pipi"的用戶。
運行程序:
user1:&{1?dj?salt?18?12345?2020-05-08?21:12:11?+0800?CST?2020-05-08?21:12:11?+0800?CST}user2:&{1?dj?salt?18?12345?2020-05-08?21:12:11?+0800?CST?2020-05-08?21:12:11?+0800?CST}
user3:&{5?mxg?salt?54?12345?2020-05-08?21:13:31?+0800?CST?2020-05-08?21:13:31?+0800?CST}
user4:&{3?pipi?salt?2?12345?2020-05-08?21:13:31?+0800?CST?2020-05-08?21:13:31?+0800?CST}
查詢條件的使用不區(qū)分調(diào)用順序,但是必須在Get()方法之前調(diào)用。實際上后面介紹的查詢&統(tǒng)計方法也是如此,可以在調(diào)用實際的方法前添加一些過濾條件。除此之外xorm支持只返回指定的列(xorm.Cols())或忽略特定的列(xorm.Omit()):
func?main()?{??engine,?_?:=?xorm.NewEngine("mysql",?"root:12345@/test?charset=utf8")
??user1?:=?&User{}
??engine.ID(1).Cols("id",?"name",?"age").Get(user1)
??fmt.Printf("user1:%v\n",?user1)
??user2?:=?&User{Name:?"pipi"}
??engine.Omit("created",?"updated").Get(user2)
??fmt.Printf("user2:%v\n",?user2)
}
上面第一個查詢使用Cols()方法指定只返回id、name、age這 3 列,第二個查詢使用Omit()方法忽略列created和updated。
另外,為了便于排查可能出現(xiàn)的問題,xorm提供了ShowSQL()方法設(shè)置將執(zhí)行的 SQL 同時在控制臺中輸出:
func?main()?{??engine,?_?:=?xorm.NewEngine("mysql",?"root:12345@/test?charset=utf8")
??engine.ShowSQL(true)
??user?:=?&User{}
??engine.ID(1).Omit("created",?"updated").Get(user)
??fmt.Printf("user:%v\n",?user)
}
運行程序:
[xorm]?[info]??2020/05/08?21:38:29.349976?[SQL]?SELECT?`id`,?`name`,?`salt`,?`age`,?`passwd`?FROM?`user`?WHERE?`id`=??LIMIT?1?[1]?-?4.0033msuser:&{1?dj?salt?18?12345?0001-01-01?00:00:00?+0000?UTC?0001-01-01?00:00:00?+0000?UTC}
由輸出可以看出,執(zhí)行的 SQL 語句為:
SELECT?`id`,?`name`,?`salt`,?`age`,?`passwd`?FROM?`user`?WHERE?`id`=??LIMIT?1該語句耗時 4.003 ms。在開發(fā)中這個方法非常好用!
有時候,調(diào)試信息都輸出到控制臺并不利于我們查詢,xorm可以設(shè)置日志選項,將日志輸出到文件中:
func?main()?{??engine,?_?:=?xorm.NewEngine("mysql",?"root:12345@/test?charset=utf8")
??f,?err?:=?os.Create("sql.log")
??if?err?!=?nil?{
????panic(err)
??}
??engine.SetLogger(log.NewSimpleLogger(f))
??engine.Logger().SetLevel(log.LOG_DEBUG)
??engine.ShowSQL(true)
??user?:=?&User{}
??engine.ID(1).Omit("created",?"updated").Get(user)
??fmt.Printf("user:%v\n",?user)
}
這樣xorm就會將調(diào)試日志輸出到sql.log文件中。注意log.NewSimpleLogger(f)是xorm的子包xorm.io/xorm/log提供的簡單日志封裝,而非標(biāo)準(zhǔn)庫log。
Exist
Exist()方法查詢符合條件的記錄是否存在,它的返回與Get()方法一致,都是(bool, error)。不同之處在于Get()會將查詢得到的字段賦值給傳入的對象。相比之下Exist()方法效率要高一些。如果不需要獲取數(shù)據(jù),只要判斷是否存在建議使用Exist()方法。
func?main()?{??engine,?_?:=?xorm.NewEngine("mysql",?"root:12345@/test?charset=utf8")
??user1?:=?&User{}
??has,?_?:=?engine.ID(1).Exist(user1)
??if?has?{
????fmt.Println("user?with?id=1?exist")
??}?else?{
????fmt.Println("user?with?id=1?not?exist")
??}
??user2?:=?&User{}
??has,?_?=?engine.Where("name=?",?"dj2").Get(user2)
??if?has?{
????fmt.Println("user?with?name=dj2?exist")
??}?else?{
????fmt.Println("user?with?name=dj2?not?exist")
??}
}
Find
Get()方法只能返回單條記錄,其生成的 SQL 語句總是有LIMIT 1。Find()方法返回所有符合條件的記錄。Find()需要傳入對象切片的指針或 map 的指針:
func?main()?{??engine,?_?:=?xorm.NewEngine("mysql",?"root:12345@/test?charset=utf8")
??slcUsers:=?make([]User,?1)
??engine.Where("age?>???and?age?,?12,?30).Find(&slcUsers)
??fmt.Println("users?whose?age?between?[12,30]:",?slcUsers)
??mapUsers?:=?make(map[int64]User)
??engine.Where("length(name)?=??",?3).Find(&mapUsers)
??fmt.Println("users?whose?has?name?of?length?3:",?mapUsers)
}
map的鍵為主鍵,所以如果表為復(fù)合主鍵就不能使用這種方式了。
Iterate
與Find()一樣,Iterate()也是找到滿足條件的所有記錄,只不過傳入了一個回調(diào)去處理每條記錄:
func?main()?{??engine,?_?:=?xorm.NewEngine("mysql",?"root:12345@/test?charset=utf8")
??engine.Where("age?>???and?age?,?12,?30).Iterate(&User{},?func(i?int,?bean?interface{})?error?{
????fmt.Printf("user%d:%v\n",?i,?bean.(*User))return?nil
??})
}
如果回調(diào)返回一個非nil的錯誤,后面的記錄就不會再處理了。
Count
Count()方法統(tǒng)計滿足條件的記錄數(shù)量:
func?main()?{??engine,?_?:=?xorm.NewEngine("mysql",?"root:12345@/test?charset=utf8")
??num,?_?:=?engine.Where("age?>=??",?50).Count(&User{})
??fmt.Printf("there?are?%d?users?whose?age?>=?50",?num)
}
Rows
Rows()方法與Iterate()類似,不過返回一個Rows對象由我們自己迭代,更加靈活:
func?main()?{??engine,?_?:=?xorm.NewEngine("mysql",?"root:12345@/test?charset=utf8")
??rows,?_?:=?engine.Where("age?>???and?age?,?12,?30).Rows(&User{})defer?rows.Close()
??u?:=?&User{}for?rows.Next()?{
????rows.Scan(u)
????fmt.Println(u)
??}
}
Rows()的使用與database/sql有些類似,但是rows.Scan()方法可以傳入一個對象,比database/sql更方便。
Sum
xorm提供了兩組求和的方法:
- Sum/SumInt:求某個字段的和,Sum返回float64,SumInt返回int64;
- Sums/SumsInt:分別求某些字段的和,Sums返回[]float64,SumsInt返回[]int64。
例如:
type?Sum?struct?{??Id????int64
??Money?int32
??Rate??float32
}
func?main()?{
??engine,?_?:=?xorm.NewEngine("mysql",?"root:12345@/test?charset=utf8")
??engine.Sync2(&Sum{})
??var?slice?[]*Sum
??for?i?:=?0;?i?100;?i++?{
????slice?=?append(slice,?&Sum{
??????Money:?rand.Int31n(10000),
??????Rate:??rand.Float32(),
????})
??}
??engine.Insert(&slice)
??totalMoney,?_?:=?engine.SumInt(&Sum{},?"money")
??fmt.Println("total?money:",?totalMoney)
??totalRate,?_?:=?engine.Sum(&Sum{},?"rate")
??fmt.Println("total?rate:",?totalRate)
??totals,?_?:=?engine.Sums(&Sum{},?"money",?"rate")
??fmt.Printf("total?money:%f?&?total?rate:%f",?totals[0],?totals[1])
}
插入
使用engine.Insert()方法,可以插入單條數(shù)據(jù),也可以批量插入多條數(shù)據(jù):
func?main()?{??engine,?_?:=?xorm.NewEngine("mysql",?"root:12345@/test?charset=utf8")
??user?:=?&User{Name:?"lzy",?Age:?50}
??affected,?_?:=?engine.Insert(user)
??fmt.Printf("%d?records?inserted,?user.id:%d\n",?affected,?user.Id)
??users?:=?make([]*User,?2)
??users[0]?=?&User{Name:?"xhq",?Age:?41}
??users[1]?=?&User{Name:?"lhy",?Age:?12}
??affected,?_?=?engine.Insert(&users)
??fmt.Printf("%d?records?inserted,?id1:%d,?id2:%d",?affected,?users[0].Id,?users[1].Id)
}
插入單條記錄傳入一個對象指針,批量插入傳入一個切片。需要注意的是,批量插入時,每個對象的Id字段不會被自動賦值,所以上面最后一行輸出id1和id2均為 0。另外,一次Insert()調(diào)用可以傳入多個參數(shù),可以對應(yīng)不同的表。
更新
更新通過engine.Update()實現(xiàn),可以傳入結(jié)構(gòu)指針或map[string]interface{}。對于傳入結(jié)構(gòu)體指針的情況,xorm只會更新非空的字段。如果一定要更新空字段,需要使用Cols()方法顯示指定更新的列。使用Cols()方法指定列后,即使字段為空也會更新:
func?main()?{??engine,?_?:=?xorm.NewEngine("mysql",?"root:12345@/test?charset=utf8")
??engine.ID(1).Update(&User{Name:?"ldj"})
??engine.ID(1).Cols("name",?"age").Update(&User{Name:?"dj"})
??engine.Table(&User{}).ID(1).Update(map[string]interface{}{"age":?18})
}
由于使用map[string]interface{}類型的參數(shù),xorm無法推斷表名,必須使用Table()方法指定。第一個Update()方法只會更新name字段,其他空字段不更新。第二個Update()方法會更新name和age兩個字段,age被更新為 0。
刪除
直接調(diào)用engine.Delete()刪除符合條件的記錄,返回刪除的條目數(shù)量:
func?main()?{??engine,?_?:=?xorm.NewEngine("mysql",?"root:12345@/test?charset=utf8")
??affected,?_?:=?engine.Where("name?=??",?"lzy").Delete(&User{})
??fmt.Printf("%d?records?deleted",?affected)
}
創(chuàng)建時間、更新時間、軟刪除
如果我們?yōu)閠ime.Time/int/int64這些類型的字段設(shè)置xorm:"created"標(biāo)簽,插入數(shù)據(jù)時,該字段會自動更新為當(dāng)前時間;
如果我們?yōu)閠iem.Time/int/int64這些類型的字段設(shè)置xorm:"updated"標(biāo)簽,插入和更新數(shù)據(jù)時,該字段會自動更新為當(dāng)前時間;
如果我們?yōu)閠ime.Time類型的字段設(shè)置了xorm:"deleted"標(biāo)簽,刪除數(shù)據(jù)時,只是設(shè)置刪除時間,并不真正刪除記錄。
type?Player?struct?{??Id?int64
??Name?string
??Age?int
??CreatedAt?time.Time?`xorm:"created"`
??UpdatedAt?time.Time?`xorm:"updated"`
??DeletedAt?time.Time?`xorm:"deleted"`
}
func?main()?{
??engine,?_?:=?xorm.NewEngine("mysql",?"root:12345@/test?charset=utf8")
??engine.Sync2(&Player{})
??engine.Insert(&Player{Name:"dj",?Age:18})
??p?:=?&Player{}
??engine.Where("name?=??",?"dj").Get(p)
??fmt.Println("after?insert:",?p)
??time.Sleep(5?*?time.Second)
??engine.Table(&Player{}).ID(p.Id).Update(map[string]interface{}{"age":30})
??engine.Where("name?=??",?"dj").Get(p)
??fmt.Println("after?update:",?p)
??time.Sleep(5?*?time.Second)
??engine.ID(p.Id).Delete(&Player{})
??engine.Where("name?=??",?"dj").Unscoped().Get(p)
??fmt.Println("after?delete:",?p)
}
輸出:
after?insert:?&{1?dj?18?2020-05-08?23:09:19?+0800?CST?2020-05-08?23:09:19?+0800?CST?0001-01-01?00:00:00?+0000?UTC}after?update:?&{1?dj?30?2020-05-08?23:09:19?+0800?CST?2020-05-08?23:09:24?+0800?CST?0001-01-01?00:00:00?+0000?UTC}
after?delete:?&{1?dj?30?2020-05-08?23:09:19?+0800?CST?2020-05-08?23:09:24?+0800?CST?2020-05-08?23:09:29?+0800?CST}
創(chuàng)建時間一旦創(chuàng)建成功就不會再改變了,更新時間每次更新都會變化。已刪除的記錄必須使用Unscoped()方法查詢,如果要真正 刪除某條記錄,也可以使用Unscoped()。
執(zhí)行原始的 SQL
除了上面提供的方法外,xorm還可以執(zhí)行原始的 SQL 語句:
func?main()?{??engine,?_?:=?xorm.NewEngine("mysql",?"root:12345@/test?charset=utf8")
??querySql?:=?"select?*?from?user?limit?1"
??reuslts,?_?:=?engine.Query(querySql)
??for?_,?record?:=?range?reuslts?{
????for?key,?val?:=?range?record?{
??????fmt.Println(key,?string(val))
????}
??}
??updateSql?:=?"update?`user`?set?name=??where?id=?"
??res,?_?:=?engine.Exec(updateSql,?"ldj",?1)
??fmt.Println(res.RowsAffected())
}
Query()方法返回[]map[string][]byte,切片中的每個元素都代表一條記錄,map的鍵對應(yīng)列名,[]byte為值。還有QueryInterface()方法返回[]map[string]interface{},QueryString()方法返回[]map[string]interface{}。
運行程序:
salt?saltage?18
passwd?12345
created?2020-05-08?21:12:11
updated?2020-05-08?22:44:58
id?1
name?ldj
1?<nil>
總結(jié)
本文對xorm做了一個簡單的介紹,xorm的特性遠(yuǎn)不止于此。xorm可以定義結(jié)構(gòu)體字段與表列名映射規(guī)則、創(chuàng)建索引、執(zhí)行事務(wù)、導(dǎo)入導(dǎo)出 SQL 腳本等。感興趣可自行探索。好在xorm有比較詳盡的中文文檔。
大家如果發(fā)現(xiàn)好玩、好用的 Go 語言庫,歡迎到 Go 每日一庫 GitHub 上提交 issue?
參考
推薦閱讀
項目使用了 ORM,具體執(zhí)行的是什么 SQL 語句總是很迷?xorm1.0 解決了
喜歡本文的朋友,歡迎關(guān)注“Go語言中文網(wǎng)”:
Go語言中文網(wǎng)啟用微信學(xué)習(xí)交流群,歡迎加微信:274768166,投稿亦歡迎
創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎勵來咯,堅持創(chuàng)作打卡瓜分現(xiàn)金大獎總結(jié)
以上是生活随笔為你收集整理的go get如何删除_Go 每日一库之 xorm的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Win11系统自动暂停更新后想继续怎么办
- 下一篇: java 自旋方法_JAVA循环使用CA