序言
本文主要介绍了Go框架三件套:GORM、Kitex和Hertz的相关知识。
GORM是一个功能强大的ORM框架,已经存在并迭代了十多年。它能够支持数据库的操作和管理。
Kitex是字节跳动内部使用的Golang微服务RPC框架,具有高性能和强可扩展性的特点。它在字节内部被广泛使用,如果对微服务性能有要求且希望定制扩展融入自己的治理体系,Kitex是一个不错的选择。
Hertz是一个HTTP框架,参考了其他开源框架的优势,具有高易用性、高性能和高扩展性的特点。
针对上述三个框架,本文将分别介绍它们的基本使用方法。
在GORM的基本使用部分,首先定义了一个名为Student的结构体,用于存放数据库中操作的数据。然后,介绍了GORM的默认约定,例如使用ID字段作为主键、使用结构体名的蛇形命名作为表名等。接着,演示了如何连接MySQL数据库并新增数据。通过示例代码可以看出,首先需要连接数据库,然后使用db.AutoMigrate(&Student{})进行迁移,将结构体与数据库建立联系,最后使用db.Create(&student)新增数据。
三件套使用
Gorm的基本使用
对结构体Student的定义和通过实现Tabler接口来更改表名的示例:
type Student struct {
Id int64 `gorm:"primaryKey"` // 指定id为主键,默认为 "ID"
Name string `gorm:"column:name"` // 指定字段名为 "name"
Password string `gorm:"column:password"`
Age int64 `gorm:"column:age"`
CreatedAt time.Time `gorm:"column:create_at"` // 蛇形命名
}
// 实现Tabler接口,更改默认表名为 "student"
func (Student) TableName() string {
return "student"
}
以上代码定义了一个名为Student的结构体,包含了Id、Name、Password、Age和CreatedAt等属性。在结构体中,通过使用标签(gorm tag)指定了字段的约束和特性,例如设置Id为主键、指定字段名为"name"等。另外,通过实现Tabler接口并重写TableName方法,我们可以将默认的表名"students"更改为"student"。
这样,根据GORM的约定,默认会使用结构体名的蛇形命名作为表名,在这个例子中,通过重写TableName方法,表名被修改为"student"。在数据库迁移或查询过程中,GORM会根据这些约定和定义来进行相应的操作。
Gorm的默认约定
GORM的默认约定包括以下内容:
- GORM将使用名为ID的字段作为主键。
- GORM将使用结构体的蛇形命名作为表名。例如,结构体Student对应的表名将默认为"students"。
- GORM将使用字段名的蛇形命名作为列名。例如,结构体Student中的字段Name将对应数据库表中的"name"列。
- GORM将使用CreatedAt和UpdatedAt字段来跟踪记录的创建和更新时间。
这些约定使得GORM能够在不需要显式配置的情况下,根据结构体的定义自动进行数据库操作。重新表达后,以上就是GORM的默认约定的内容。
连接MySql数据库 并新增数据
GORM支持MySQL、SQL SErver、PostgreSQL、SQLite数据库,此处以连接Mysql为例。
func main() {
// 连接数据库
dsn := "root:password@tcp(localhost:3306)/database_name?charset=utf8mb4&parseTime=True&loc=Local"
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
if err != nil {
fmt.Printf("数据库连接失败:%s", err.Error())
return
}
// 迁移
db.AutoMigrate(&Student{})
// 新增数据
student := Student{
ID: 1,
Name: "xiaoming",
Password: "123456",
Age: 13,
CreatedAt: time.Now(),
}
result := db.Create(&student)
if result.Error != nil {
fmt.Printf("新增数据失败:%s", result.Error.Error())
return
}
fmt.Println("执行的记录条数:", result.RowsAffected)
}
输出结果:
执行的有些条数: 1
以上示例中,我们首先通过gorm.Open打开MySQL数据库的连接。其中dsn是连接字符串,需要根据实际情况进行配置。然后,使用db.AutoMigrate(&Student{})进行迁移,将定义的Student结构体与数据库建立联系。接下来,创建一个Student实例并赋值相应的字段。最后,使用db.Create(&student)将数据插入到数据库中,并通过结果对象result获取执行的记录条数。
当代码运行后,将输出执行的记录条数。如果连接或新增数据过程中出现错误,将输出相应的错误信息。
数据库中创建了数据库表student,并新增了数据
使用GORM处理数据冲突以及批量新增数据:
// 处理数据冲突
student := Student{ID: 2, Name: "小舞"}
db.Clauses(clause.OnConflict{DoNothing: true}).Create(&student)
// 批量新增数据
students := []Student{
{ID: 2, Name: "张三", Password: "333333", Age: 33, CreatedAt: time.Now()},
{ID: 3, Name: "李四", Password: "444444", Age: 44, CreatedAt: time.Now()},
{ID: 4, Name: "王五", Password: "555555", Age: 55, CreatedAt: time.Now()},
}
result := db.Create(&students)
fmt.Println("执行的记录条数:", result.RowsAffected)
**
以上示例中,第一个部分演示了如何处理数据冲突。我们创建了一个名为student的Student实例,使用clause.OnConflict{DoNothing: true}来处理冲突,即在冲突发生时不执行任何操作,并通过db.Clauses().Create()将该实例插入到数据库。
第二个部分演示了批量新增数据的方法。我们定义了一个名为students的Student切片,其中包含了多个Student实例。通过db.Create()方法将这些实例一次性批量插入到数据库中,并通过result.RowsAffected获取执行的记录条数。
运行代码后,将输出执行的记录条数。在数据冲突处理的示例中,因为冲突发生并设置为不执行操作,所以输出的记录条数为0。在批量新增数据的示例中,因为插入了三条数据,所以输出的记录条数为3。
查看数据库
修改
// 修改数据
result := db.Model(&Student{}).Where("id = ?", 1).Update("name", "唐三")
if result.Error != nil {
fmt.Printf("修改数据失败:%s", result.Error.Error())
return
}
fmt.Println("执行的记录条数:", result.RowsAffected)
查询
在示例中,使用db.Model(&Student{})指定要操作的模型,并通过Where("id = ?", 1)指定要修改的记录条件,即ID为1。然后,使用Update("name", "唐三")将"name"字段更新为"唐三"。result.RowsAffected用于获取执行的记录条数。
提供了如下五种查询方式。
// 方式一:获取第一条记录(按主键升序)
var student1 Student
db.First(&student1)
fmt.Println("方式一 查询的student1为:", student1)
// 方式二:获取一条记录(未指定排序字段)
var student2 Student
db.Take(&student2)
fmt.Println("方式二 查询的student2为:", student2)
// 方式三:获取最后一条记录(按主键降序)
var student3 Student
db.Last(&student3)
fmt.Println("方式三 查询的student3为:", student3)
// 方式四:根据主键查询
var student4 Student
db.First(&student4, 3)
fmt.Println("方式四 查询的student4为:", student4)
// 方式五:查询全部记录
var students []Student
db.Find(&students)
fmt.Println("方式五 查询的所有学生数据如下:")
for _, student := range students {
fmt.Println(student)
}
在示例中,我们通过不同的方式查询数据库中的记录。方式一使用First()方法获取第一条记录,方式二使用Take()方法获取一条记录(未指定排序字段),方式三使用Last()方法获取最后一条记录,方式四使用First()方法根据主键查询指定的记录,方式五使用Find()方法获取全部的记录。
在方式一至方式四的查询中,我们创建了相应的student变量,并通过查询方法给它们赋值。在方式五的查询中,我们创建了一个students切片,并通过Find()方法将所有的记录赋值给它。
完成查询后,我们通过fmt.Println()将查询结果输出到控制台。
删除
var students []Student
db.Find(&students)
fmt.Println("删除前数据如下:")
for i := 0; i < len(students); i++ {
fmt.Println(students[i])
}
// 根据ID删除
student1 := Student{ID: 3}
db.Delete(&student1)
// 根据用户名删除
student2 := Student{}
db.Where("name = ?", "王五").Delete(&student2)
db.Find(&students)
fmt.Println("删除后数据如下:")
for i := 0; i < len(students); i++ {
fmt.Println(students[i])
}
// 使用事务执行写入操作
db.Transaction(func(tx *gorm.DB) error {
if err := tx.Create(&Student{ID: 5, Name: "小明"}).Error; err != nil {
return err
}
if err := tx.Create(&Student{ID: 6, Name: "小红"}).Error; err != nil {
return err
}
return nil // 返回nil提交事务
})
在示例中,我们首先通过Find()方法查询数据库中的学生数据,并输出到控制台。然后,我们使用Delete()方法根据ID和用户名进行删除操作。通过创建对应的student实例,并传递给Delete()方法来指定条件删除。完成删除后,我们再次使用Find()方法查询数据库并输出结果。
接下来,我们演示了使用事务执行写入操作的示例。通过db.Transaction()方法开启事务,在事务中执行创建学生的操作。如果在事务过程中发生错误,事务会自动回滚,否则会提交事务。
性能优化
- 关闭事务;
- 使用PrepareStmt 缓存预编译语句可以提高调用速度,可提升大约35%左右;
学习链接 gorm.cn/zh_CN/docs/…
GORM生态
GORM 拥有丰富的扩展生态,部分举例如下: Gorm文档:gorm.cn