有偷菜餐厅城市建设的网站,好的手机网站,网站搭建合作协议,百姓网创建不了位置交易地点简介Go 标准库提供的数据库接口database/sql比较底层#xff0c;使用它来操作数据库非常繁琐#xff0c;而且容易出错。因而社区开源了不少第三方库#xff0c;如上一篇文章中的sqlc工具#xff0c;还有各式各样的 ORM (Object Relational Mapping#xff0c;对象关系映射… 简介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 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?charsetutf8) 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表中插入了一些数据后面的代码为了简单起见忽略了错误处理实际使用中不要漏掉GetGet()方法用于查询单条数据并使用返回的字段为传入的对象赋值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?charsetutf8) 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字段为 5engine.Get(user3)查询id 5的用户user4设置了字段Name为pipiengine.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?charsetutf8) 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?charsetutf8) 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?charsetutf8) 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。ExistExist()方法查询符合条件的记录是否存在它的返回与Get()方法一致都是(bool, error)。不同之处在于Get()会将查询得到的字段赋值给传入的对象。相比之下Exist()方法效率要高一些。如果不需要获取数据只要判断是否存在建议使用Exist()方法。func main() { engine, _ : xorm.NewEngine(mysql, root:12345/test?charsetutf8) user1 : User{} has, _ : engine.ID(1).Exist(user1) if has { fmt.Println(user with id1 exist) } else { fmt.Println(user with id1 not exist) } user2 : User{} has, _ engine.Where(name?, dj2).Get(user2) if has { fmt.Println(user with namedj2 exist) } else { fmt.Println(user with namedj2 not exist) }}FindGet()方法只能返回单条记录其生成的 SQL 语句总是有LIMIT 1。Find()方法返回所有符合条件的记录。Find()需要传入对象切片的指针或 map 的指针func main() { engine, _ : xorm.NewEngine(mysql, root:12345/test?charsetutf8) 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?charsetutf8) 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的错误后面的记录就不会再处理了。CountCount()方法统计满足条件的记录数量func main() { engine, _ : xorm.NewEngine(mysql, root:12345/test?charsetutf8) num, _ : engine.Where(age ?, 50).Count(User{}) fmt.Printf(there are %d users whose age 50, num)}RowsRows()方法与Iterate()类似不过返回一个Rows对象由我们自己迭代更加灵活func main() { engine, _ : xorm.NewEngine(mysql, root:12345/test?charsetutf8) 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更方便。Sumxorm提供了两组求和的方法Sum/SumInt求某个字段的和Sum返回float64SumInt返回int64Sums/SumsInt分别求某些字段的和Sums返回[]float64SumsInt返回[]int64。例如type Sum struct { Id int64 Money int32 Rate float32}func main() { engine, _ : xorm.NewEngine(mysql, root:12345/test?charsetutf8) 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?charsetutf8) 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?charsetutf8) 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?charsetutf8) 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?charsetutf8) 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?charsetutf8) 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 GitHubhttps://github.com/go-xorm/xormxorm 手册http://gobook.io/read/gitea.com/xorm/manual-zh-CN/Go 每日一库 GitHubhttps://github.com/darjun/go-daily-lib推荐阅读项目使用了 ORM具体执行的是什么 SQL 语句总是很迷xorm1.0 解决了喜欢本文的朋友欢迎关注“Go语言中文网”Go语言中文网启用微信学习交流群欢迎加微信274768166投稿亦欢迎