这是我参与「第三届青训营 -后端场」笔记创作活动的的第3篇笔记
任务目标
任务一: 首先实现一个脚本工具: 包含一个 User struct, 只包含 UUID string, Name string,Age int,Version int 四个字段,在脚本中使用 gorm + mysql 初始化 DB, 并使用初始化后的 DB 的 AutoMigrate 迁移数据表。
任务二: 然后完成一个 Gen (github.com/go-gorm/gen/) 项目 实现:
-
基于上面创建的数据库及表名,通过 Gen 的自动同步库表功能生成 struct People,并给该 struct 生成基本 CRUD 方法。
-
基于 OnConflict Upsert 功能实现 100 个随机用户的创建,其中需要包含重复的 UUID 用户的 Upsert, 在 Upsert 时,如果遇到重复 UUID 中,需要将 Version 更新为 Version + 1。
-
最后再通过一条自定义的Raw SQL 实现,将数据按 Version 分组,并取出 Version 最高的一组的用户总数的功能,该 Raw SQL 需要通过自定义查询方法的形式实现,需要给 People 生成相应的方法名: GetMaxVersionCount
任务的具体实现代码已经在我的github上了
任务1
任务一较为简单,只需要掌握gorm的基本用法即可。
gorm.Open方法会返回一个*DB的结构体和error,同时自动初始化数据库
DB.AutoMigrate方法会自动的生成和传入结构体格式相同的表。参数可以是多个,表格命名为结构体的名字
package main
import (
"log"
"gorm.io/driver/mysql"
"gorm.io/gorm"
)
type User struct {
UUID string
Name string
Age int
Version int
}
//作业1
func Init() {
drive, err := gorm.Open(
mysql.New(mysql.Config{
DSN: "root:123456@tcp(127.0.0.1:3306)/gorm?charset=utf8&parseTime=True&loc=Local",
DefaultStringSize: 256, // string 类型字段的默认长度
DisableDatetimePrecision: true, // 禁用 datetime 精度,MySQL 5.6 之前的数据库不支持
DontSupportRenameIndex: true, // 重命名索引时采用删除并新建的方式,MySQL 5.7 之前的数据库和 MariaDB 不支持重命名索引
DontSupportRenameColumn: true, // 用 `change` 重命名列,MySQL 8 之前的数据库和 MariaDB 不支持重命名列
SkipInitializeWithVersion: false, // 根据当前 MySQL 版本自动配置
}), &gorm.Config{})
if err != nil {
panic(err)
}
err = drive.AutoMigrate(&User{})
if err != nil {
log.Fatal(err)
return
}
}
func main() {
Init()
}
任务二
自动生成struct People,并生成基本CRUD方法
*DB提供的GenerateModelAs可以自动生成相应表结构的struct,返回的是BaseStruct的指针,传入的第一个参数为表名。其余参数可缺省,则生成的结构体名和表名一致。若有多个参数,则第二个参数为生成的结构体的名字,后续的为属性名
ApplyBasic接收
package main
import (
"gorm.io/driver/mysql"
"gorm.io/gen"
"gorm.io/gorm"
)
func generateModel() {
g := gen.NewGenerator(gen.Config{
OutPath: "../../dal/query",
})
db, _ := gorm.Open(mysql.Open("root:123456@tcp(127.0.0.1:3306)/gorm?charset=utf8mb4&parseTime=True&loc=Local"))
g.UseDB(db)
//读取数据库中的users表,生成People结构体,并返回BaseStruct地址
// g.GenerateModelAs("users", "People")
//ApplyBasic的参数是&BaseStruct,生成并生成对应的基本CRUD方法
g.ApplyBasic(g.GenerateModelAs("users", "Person"))
g.Execute()
}
func main() {
generateModel()
}
基于 OnConflict Upsert 功能实现 100 个随机用户的创建
对于这部操作,在我使用OnConflict方法更新Version会有各种报错,所以我选择了直接在插入之前查询是否有相同的UUID,若没有则直接插入,若有则version+1
因为要包含重复的UUID,所以我选择生成50个不同的UUID,再随机从50个中选择一个作为创建的用户的UUID
package biz
import (
"context"
"go-gorm/gen/dal"
"go-gorm/gen/dal/model"
"go-gorm/gen/dal/query"
"log"
"math/rand"
"github.com/google/uuid"
"gorm.io/gorm"
)
//插入100条数据,重复的UUID则Version+1
func InsertPerson() {
conn := dal.Connect()
query := query.Use(conn)
// fmt.Printf("query.Person.ALL: %v\n", query.Person.ALL)
UUID := make([]string, 50)
for i := range UUID {
u, err := uuid.NewUUID()
if err != nil {
log.Fatal(err)
}
UUID[i] = u.String()
}
for i := 0; i < 100; i++ {
j := rand.Intn(50)
u, err := query.Person.WithContext(context.Background()).Debug().Where(query.Person.UUID.Eq(UUID[j])).First()
if err != nil {
if err == gorm.ErrRecordNotFound {
err = query.Person.WithContext(context.Background()).Create(&model.Person{
UUID: UUID[j],
Name: "2",
Age: 1,
Version: 1,
})
if err != nil {
log.Printf("Create fail:%v\n", err)
}
continue
}
log.Printf("Find fail:%v\n", err)
continue
}
_, err = query.Person.WithContext(context.Background()).Where(query.Person.UUID.Eq(UUID[j])).Update(query.Person.Version, u.Version+1)
if err != nil {
log.Printf("Update fail:%v\n", err)
continue
}
}
}
取出Version最高的一组的用户的数量
这一步就很简单了,分组再排序就可
func (p personDo) GetMaxVersionCount() (result *model.Person, err error) {
var generateSQL strings.Builder
generateSQL.WriteString("select * from users order by version desc ")
var executeSQL *gorm.DB
executeSQL = p.UnderlyingDB().Raw(generateSQL.String()).Take(&result)
err = executeSQL.Error
return
}