go get如何删除_Go 每日一库之 xorm
简介
Go 标准库提供的数据库接口database/sql
比较底层,使用它来操作数据库非常繁琐,而且容易出错。因而社区开源了不少第三方库,如上一篇文章中的sqlc
工具,还有各式各样的 ORM (Object Relational Mapping,对象关系映射库),如gorm
和xorm
。本文介绍xorm
。xorm
是一个简单但强大的 Go 语言 ORM 库,使用它可以大大简化我们的数据库操作。
快速使用
先安装:
$ go get xorm.io/xorm
由于需要操作具体的数据库(本文中我们使用 MySQL),需要安装对应的驱动:
$ go get github.com/go-sql-driver/mysql
使用:
package main
import ( "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
来操作数据库,首先需要使用xorm.NewEngine()
创建一个引擎。该方法的参数与sql.Open()
参数相同。
上面代码中,我们演示了xorm
的一个非常实用的功能,将数据库中的表与对应 Go 代码中的结构体做同步。初始状态下,数据库test
中没有表user
,调用Sync2()
方法会根据User
的结构自动创建一个user
表。执行后,通过describe user
查看表结构:
如果表user
已经存在,Sync()
方法会对比User
结构与表结构的不同,对表做相应的修改。我们给User
结构添加一个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"`}
再次执行这个程序后,用describe user
命令查看表结构:
发现表中多了一个level
字段。
**此修改只限于添加字段。**删除表中已有的字段会带来比较大的风险。如果我们User
结构的Salt
字段删除,然后执行程序。出现下面错误:
[xorm] [warn] 2020/05/07 22:44:38.528784 Table user has column salt but struct has not related field
数据库操作
查询&统计
xorm
提供了几个查询和统计方法,Get/Exist/Find/Iterate/Count/Rows/Sum
。下面逐一介绍。
为了代码演示方便,我在user
表中插入了一些数据:
后面的代码为了简单起见,忽略了错误处理,实际使用中不要漏掉!
Get
Get()
方法用于查询单条数据,并使用返回的字段为传入的对象赋值:
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
设置了Id
字段为 5,engine.Get(user3)
查询id = 5
的用户;user4
设置了字段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}
查询条件的使用不区分调用顺序,但是必须在Get()
方法之前调用。实际上后面介绍的查询&统计方法也是如此,可以在调用实际的方法前添加一些过滤条件。除此之外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
。
另外,为了便于排查可能出现的问题,xorm
提供了ShowSQL()
方法设置将执行的 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}
由输出可以看出,执行的 SQL 语句为:
SELECT `id`, `name`, `salt`, `age`, `passwd` FROM `user` WHERE `id`=? LIMIT 1
该语句耗时 4.003 ms。在开发中这个方法非常好用!
有时候,调试信息都输出到控制台并不利于我们查询,xorm
可以设置日志选项,将日志输出到文件中:
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
就会将调试日志输出到sql.log
文件中。注意log.NewSimpleLogger(f)
是xorm
的子包xorm.io/xorm/log
提供的简单日志封装,而非标准库log
。
Exist
Exist()
方法查询符合条件的记录是否存在,它的返回与Get()
方法一致,都是(bool, error)
。不同之处在于Get()
会将查询得到的字段赋值给传入的对象。相比之下Exist()
方法效率要高一些。如果不需要获取数据,只要判断是否存在建议使用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
的键为主键,所以如果表为复合主键就不能使用这种方式了。
Iterate
与Find()
一样,Iterate()
也是找到满足条件的所有记录,只不过传入了一个回调去处理每条记录:
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 })}
如果回调返回一个非nil
的错误,后面的记录就不会再处理了。
Count
Count()
方法统计满足条件的记录数量:
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()
方法,可以插入单条数据,也可以批量插入多条数据:
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()
调用可以传入多个参数,可以对应不同的表。
更新
更新通过engine.Update()
实现,可以传入结构指针或map[string]interface{}
。对于传入结构体指针的情况,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{}
类型的参数,xorm
无法推断表名,必须使用Table()
方法指定。第一个Update()
方法只会更新name
字段,其他空字段不更新。第二个Update()
方法会更新name
和age
两个字段,age
被更新为 0。
删除
直接调用engine.Delete()
删除符合条件的记录,返回删除的条目数量:
func main() { engine, _ := xorm.NewEngine("mysql", "root:12345@/test?charset=utf8")
affected, _ := engine.Where("name = ?", "lzy").Delete(&User{}) fmt.Printf("%d records deleted", affected)}
创建时间、更新时间、软删除
如果我们为time.Time/int/int64
这些类型的字段设置xorm:"created"
标签,插入数据时,该字段会自动更新为当前时间;
如果我们为tiem.Time/int/int64
这些类型的字段设置xorm:"updated"
标签,插入和更新数据时,该字段会自动更新为当前时间;
如果我们为time.Time
类型的字段设置了xorm:"deleted"
标签,删除数据时,只是设置删除时间,并不真正删除记录。
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}
创建时间一旦创建成功就不会再改变了,更新时间每次更新都会变化。已删除的记录必须使用Unscoped()
方法查询,如果要真正 删除某条记录,也可以使用Unscoped()
。
执行原始的 SQL
除了上面提供的方法外,xorm
还可以执行原始的 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
的键对应列名,[]byte
为值。还有QueryInterface()
方法返回[]map[string]interface{}
,QueryString()
方法返回[]map[string]interface{}
。
运行程序:
salt saltage 18passwd 12345created 2020-05-08 21:12:11updated 2020-05-08 22:44:58id 1name ldj1 <nil>
总结
本文对xorm
做了一个简单的介绍,xorm
的特性远不止于此。xorm
可以定义结构体字段与表列名映射规则、创建索引、执行事务、导入导出 SQL 脚本等。感兴趣可自行探索。好在xorm
有比较详尽的中文文档。
大家如果发现好玩、好用的 Go 语言库,欢迎到 Go 每日一库 GitHub 上提交 issue?
参考
- xorm GitHub:https://github.com/go-xorm/xorm
- xorm 手册:http://gobook.io/read/gitea.com/xorm/manual-zh-CN/
- Go 每日一库 GitHub:https://github.com/darjun/go-daily-lib
推荐阅读
项目使用了 ORM,具体执行的是什么 SQL 语句总是很迷?xorm1.0 解决了
喜欢本文的朋友,欢迎关注“Go语言中文网”:
Go语言中文网启用微信学习交流群,欢迎加微信:274768166,投稿亦欢迎
go get如何删除_Go 每日一库之 xorm相关推荐
- go get 的不再src目录中_Go 每日一库之 sqlc:根据 sql 生成代码
简介 在 Go 语言中编写数据库操作代码真的非常痛苦!database/sql标准库提供的都是比较底层的接口.我们需要编写大量重复的代码.大量的模板代码不仅写起来烦,而且还容易出错.有时候字段类型修改 ...
- go 根据输入类型执行对应的方法_Go 每日一库之 sqlc
简介 在 Go 语言中编写数据库操作代码真的非常痛苦!database/sql标准库提供的都是比较底层的接口.我们需要编写大量重复的代码.大量的模板代码不仅写起来烦,而且还容易出错.有时候字段类型修改 ...
- go float64 比较_Go 每日一库之 plot
Go 每日一库之 plot 简介 本文介绍 Go 语言的一个非常强大.好用的绘图库--plot.plot内置了很多常用的组件,基本满足日常需求.同时,它也提供了定制化的接口,可以实现我们的个性化需求. ...
- Go 每日一库之 xorm
简介 Go 标准库提供的数据库接口database/sql比较底层,使用它来操作数据库非常繁琐,而且容易出错.因而社区开源了不少第三方库,如上一篇文章中的sqlc工具,还有各式各样的 ORM (Obj ...
- Go 每日一库之 viper
简介 上一篇文章介绍 cobra 的时候提到了 viper,今天我们就来介绍一下这个库. viper 是一个配置解决方案,拥有丰富的特性: 支持 JSON/TOML/YAML/HCL/envfile/ ...
- Go 每日一库之 resty
简介 resty是 Go 语言的一个 HTTP client 库.resty功能强大,特性丰富.它支持几乎所有的 HTTP 方法(GET/POST/PUT/DELETE/OPTION/HEAD/PAT ...
- Go 每日一库之 fsnotify
简介 上一篇文章Go 每日一库之 viper中,我们介绍了 viper 可以监听文件修改进而自动重新加载.其内部使用的就是fsnotify这个库,它是跨平台的.今天我们就来介绍一下它. 快速使用 先安 ...
- go get 失败 no go files in_Go 每日一库之 dig
简介 今天我们来介绍 Go 语言的一个依赖注入(DI)库--dig.dig 是 uber 开源的库.Java 依赖注入的库有很多,相信即使不是做 Java 开发的童鞋也听过大名鼎鼎的 Spring.相 ...
- 删除Linux的依赖库并进入救援模式恢复
删除Linux的依赖库并进入救援模式恢复 模拟一下依赖库文件被删,并进入救援模式恢复 系统:CentOS7 一.删除mv的依赖库文件 删除/lib64/libc.so.6 [root@centos7 ...
最新文章
- chrome 插件 vimium 介绍
- linux安装g++编译器_Ubuntu Desktop下配置Rosetta安装教程
- java .item,javabb-javaitem-cloud
- win10无限重启_win10系统安装无限循环如何解决_win10教程
- recv函数返回0_函数VLOOKUP与LOOKUP的PK,大战一触即发 Excel神技能!
- redis常用命令getex_Redis常用命令
- 图解 Laravel 请求的完整生命周期
- 计算机组策略没有权限,运行组策略或程序时提示没有权限
- go mysql transfer_GitHub - weiki516/go-mysql-transfer: MySQL Binlog 增量实时同步工具
- 计算机口语英语对话视频,出国常用英语口语对话视频
- 【粉丝福利】第二波 | 当当购书狂欢,满 400 减 230!下单充电速来!
- shell语言简单学习
- C++ filesystem 文件系统初体验
- L1-079 天梯赛的善良 (20 分)python
- three.js 后期处理通道postprocessing
- 学习笔记转汉字区位码查询与算法
- 条款11:在operator=中处理“自我赋值”
- 安全漏洞一内核漏洞利用
- 计算机在线给手机杀毒,国家计算机病毒应急处理中心曝光6款恶意应用 腾讯手机管家实现精准查杀...
- 解决城市交通压力,浅析车载监控技术在公交监管中的应用
热门文章
- webgis从基础到开发实践_开源WebGIS教程系列——11.1 GISLite 的开发背景与设计
- 一个请求多个响应_一个TCP连接到底可以发多少个HTTP请求?
- struts2访问jsp页面404
- git ssh拉取代码_win10下git初始安装及配置工作
- 日历对象导哪个包_微信新表情瞬间炸裂,文物表情包永恒萌呆!
- splat net_Ruby中的Splat参数
- 最长公共前缀_最长的公共前缀
- Java BigDecimal intValue()方法与示例
- flex 下对齐_Flex 布局示例
- c# 浮点数十六进制字符串_从C#中包含十六进制值的字符串数组中打印整数值...