GORM介绍
GORM(Go Object Relational Mapping)是一个用于Go语言的对象关系映射(ORM)库,它提供了简单且强大的数据库操作功能。GORM的目标是简化数据库访问和操作,提供一种简单、直观的方式来处理数据库记录。GORM的一些主要特性主要包括:
- 对象关系映射:GORM允许开发人员将数据库表映射到Go语言的结构体,并提供了一套API来进行对象和数据库记录之间的转换和操作。这样,开发人员可以使用面向对象的方式来处理数据库操作,而不需要直接编写SQL语句。
- 数据库驱动支持:GORM支持多种常用的数据库驱动,包括MySQL、PostgreSQL、SQLite、SQL Server等,开发人员可以根据项目需求选择适合的数据库。
- CRUD操作:GORM提供了简单且一致的API来执行常见的数据库操作,包括创建、读取、更新和删除。开发人员可以使用这些API来执行数据库的插入、查询、更新和删除操作。
- 关联关系:GORM支持定义和处理数据库表之间的关联关系,包括一对一、一对多和多对多关系。开发人员可以使用GORM提供的API来进行关联数据的查询和操作。
- 事务支持:GORM提供了事务管理机制,开发人员可以使用事务来确保一组数据库操作的原子性和一致性。
- 数据验证:GORM支持在进行数据库操作之前对数据进行验证,可以定义模型结构体的验证规则,确保数据的有效性和完整性。
- 钩子函数:GORM允许开发人员定义在数据库操作过程中触发的钩子函数,例如在创建记录之前或之后执行一些操作。
前置准备
这里以GORM连接MySQL数据库为例,使用GORM连接MySQL数据库之前,需要安装两个包 ,一个是mysql的数据库驱动,一个是gorm框架本身,在带有go.mod文件的目录下,打开终端,依次执行以下操作即可完成安装。
go get gorm.io/driver/mysql
go get gorm.io/gorm
连接数据库
为了与MySQL数据库建立连接,我们需要提供访问数据库所需的相关信息。在这个示例中,我们将使用账号密码进行测试。首先,我们需要指定要访问的数据库的IP地址和端口号。接下来,我们需要提供访问数据库所需的账号和密码。最后,我们需要指定要访问的数据库的名称。通过这些信息,我们可以完成使用GORM进行MySQL连接的基本操作。
为了更好地管理与数据库的连接,我们可以创建一个名为connect.go的文件。在该文件中,我们可以定义一个全局变量DB,用于存储数据库连接。此外,我们还可以使用mysqlLogger来配置日志输出级别,以便输出所有日志信息。
在连接数据库的过程中,我们可以创建一张表来演示结构体与表字段之间的映射关系。假设我们创建了一个名为students的表,包含了name、age和my_student三个字段。我们可以在定义结构体时,使用标签来指定与表字段的对应关系,例如通过gorm:"column:name"来指定结构体字段name对应表中的name字段。
需要注意的是,GORM中的AutoMigrate方法可以用于自动创建表和字段。它的作用是只新增表和字段,而不会删除或修改现有的字段。因此,如果我们在结构体中修改了字段的定义,新的字段将会被添加到表中,而不会修改原有字段的定义。
在连接过程中创建一张表,结构体到表字段定义的关系 CREATE TABLE students (name longtext,age bigint,my_student longtext)。定义结构体的时候也可以指定对应的表字段的设置。
package main
import (
"fmt"
"gorm.io/driver/mysql"
"gorm.io/gorm"
"gorm.io/gorm/logger"
"gorm.io/gorm/schema"
)
var DB *gorm.DB
var mysqlLogger logger.Interface
func main() {
username := "root"
password := "123456"
host := "127.0.0.1"
port := 3306
Dbname := "gorm"
timeout := "10s"
mysqlLogger = logger.Default.LogMode(logger.Info)
dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=utf8mb4&parseTime=True&loc=Local&timeout=%s", username, password, host, port, Dbname, timeout)
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{
NamingStrategy: schema.NamingStrategy{
TablePrefix: "",
SingularTable: false,
NoLowerCase: false,
},
Logger: mysqlLogger,
})
if err != nil {
panic("连接数据库失败, error=" + err.Error())
}
DB = db
DB.Debug().AutoMigrate(&Student{})
}
type Student struct {
ID uint `gorm:"size:3" json:"id"`
Name string `gorm:"size:8" json:"name"`
Age int `gorm:"size:3" json:"age"`
Gender bool `json:"gender"`
Email *string `gorm:"size:32" json:"email"`
}
以上代码可以简单概括为几个步骤:
-
导入必要的包:
gorm.io/driver/mysql:导入MySQL数据库驱动。gorm.io/gorm:导入GORM库本身。gorm.io/gorm/logger:导入GORM的日志模块。gorm.io/gorm/schema:导入GORM的模式定义模块。
-
定义全局变量:
DB:用于存储数据库连接实例的全局变量。mysqlLogger:用于设置日志输出级别的全局变量。
-
初始化函数(init):
-
设置数据库连接参数:
username、password、host、port、dbName、timeout分别为数据库的用户名、密码、主机地址、端口、数据库名和连接超时时间。
-
创建数据库连接字符串(dsn):
- 使用
fmt.Sprintf格式化字符串,将连接参数拼接成完整的连接字符串。
- 使用
-
打开数据库连接:
- 使用
gorm.Open方法,传入mysql.Open(dsn)作为数据库驱动,以及一个配置对象。
- 使用
-
检查连接错误:
- 如果连接错误不为空,则抛出 panic 异常。
-
将连接实例赋值给全局变量
DB。
-
创建出来的student表结构如下
从中可以看出GORM同样会使用一个结构体来充当Go与数据库之间的一个映射对象,可以从这个Student结构体。通过GORM,我们可以通过对结构体的定义和配置,创建对应的数据表。当定义一个结构体作为数据库的映射对象时,GORM会根据一定的映射规则来生成对应的数据表。例如,我们可以通过设置TablePrefix来为表名添加前缀,或者设置SingularTable来保持表名的单数模式。这使得我们可以根据项目的需求和数据库的命名规范,灵活地定义生成的数据表的名称。
另外,GORM还提供了全局的日志输出级别的配置。当我们开启Debug模式时,GORM会将实际执行的SQL语句在控制台进行输出。这对于在开发环境下进行调试和问题排查非常有帮助。通过查看输出的SQL语句,我们可以更加直观地了解程序与数据库之间的交互过程,发现潜在的问题或性能瓶颈。因此GORM在建表方面非常灵活。
操作数据库
创建一个 table_ops.go 文件来负责增删改查操作。
在创建好Student这张表后,vscode执行多个go文件时,这些文件只能有一个main入口类,可以通过多选需要执行的若干个go文件,统一执行。将connect.go中的main函数改为init函数,和table_ops.go选中后,一并执行。
可以在操作数据库的时候注册一些事件(钩子),当数据库执行指定操作的前后等时机自动触发。
package main
import (
"encoding/json"
"fmt"
"gorm.io/gorm"
)
func (user *Student) BeforeCreate(tx *gorm.DB) (err error) {
email := fmt.Sprintf("%s@qq.com", user.Name)
user.Email = &email
return nil
}
func main() {
email := "gorm@qq.com"
s1 := Student{
Name: "frank",
Age: 21,
Gender: true,
Email: &email,
}
err := DB.Create(&s1).Error
fmt.Println(err)
fmt.Println(s1)
var studentList []Student
for i := 0; i < 10; i++ {
studentList = append(studentList, Student{
Name: fmt.Sprintf("批量%d号", i+1),
Age: 21,
Gender: true,
Email: &email,
})
}
DB.Create(&studentList)
fmt.Println(studentList)
DB := DB.Session(&gorm.Session{Logger: mysqlLogger})
var student Student
DB.Take(&student)
fmt.Println(student)
student = Student{}
DB.First(&student)
fmt.Println(student)
student = Student{}
DB.Last(&student)
fmt.Println(student)
student = Student{}
DB.Take(&student, "name=?", "批量2号")
fmt.Println(student)
student = Student{}
student.ID = 5
DB.Take(&student)
fmt.Println(student)
i := DB.Find(&studentList).RowsAffected
fmt.Println(i)
b, _ := json.Marshal(studentList)
fmt.Println(string(b))
DB.Find(&studentList, []int{3, 4, 5})
fmt.Println(studentList)
DB.Find(&studentList, "name in (?)", []string{"批量5号", "frank"})
fmt.Println(studentList)
student = Student{}
DB.Take(&student, len(studentList))
student.Name = "4396"
student.Age = 88
DB.Select("name").Save(&student)
DB.Find(&studentList, []int{10, 11, 12}).Update("gender", true)
email = "newgorm@qq.com"
DB.Model(&Student{}).Where("age<?", 99).Updates(Student{
Email: &email,
Gender: false,
})
var deletedStudent Student
DB.Delete(&deletedStudent, []int{7, 9, 11})
DB.Create(&Student{
Name: "newbee",
})
}
主要进行了以下操作:增(Create)、删(Delete)、改(Update)和查(Query)。
1. 增加数据操作
-
添加单条数据, 给Student表插入新纪录(单条):
- 创建了一个
Student对象s1,表示要插入的学生信息。 - 使用
DB.Create(&s1)将s1对象插入数据库。
- 创建了一个
-
批量添加数据,给Student表插入新纪录(多条):
- 创建了一个
Student对象列表studentList,表示要插入的多个学生信息。 - 使用循环将多个学生对象添加到
studentList列表中。 - 使用
DB.Create(&studentList)批量将studentList中的学生对象插入数据库。
- 创建了一个
2.查询数据操作
-
简单查询:
- 使用
DB.Take(&student, "name=?", "批量2号")条件查询一条记录,并将查询结果存储在student对象中。 - 使用
DB.Take(&student)条件查询一条记录,并将查询结果存储在student对象中。
- 使用
-
条件查询:
- 根据参数来查询单条记录
- 根据结构体来查,提前给结构体中的属性赋值,gorm会默认识别成查询条件
- 查询多条记录
- 主键支持直接传入切片的形式查询
- 其他条件的多条查询,首先要指定查询条件,然后传入数组
3. 修改数据操作
-
修改单条数据:
- 使用
DB.Save(&student)更新一条记录。先使用DB.Take(&student, len(studentList))查询一条记录,然后修改student对象的字段值,并使用DB.Save(&student)进行更新。 - save方式:save方式是根据数据表指定的ID字段作为查询条件的,所以这个ID字段不能改变。一旦改变,查询的条件也会变,其他的字段修改的时候,会被gorm识别成update set语句。UPDATE
f_studentsSETname='1234556',age=99,email=NULL WHEREid= 99 表里最大记录数是12,所以这个语句不会对表产生任何影响。注意这里的Select函数是要修改的字段。Take获取到对应的记录,修改后,调用Save将修改完的对象指针传入。如果没有Select,Save会把整个对象的字段值都写入到表中的记录,即便这个对象的某些字段是零值。不希望用零值覆盖原本的数据行的对应字段值,就要使用Select函数
- 使用
-
批量修改数据:
- 使用
DB.Find(&studentList, []int{10, 11, 12}).Update("gender", true)批量更新多条记录。先使用DB.Find(&studentList, []int{10, 11, 12})查询多条记录,然后将这些记录的gender字段更新为true。 - find + update方式。支持简单更新某个字段的信息,在find里指定要查询到的若干条记录,然后加上update函数即可在这些对象上进行更新,第一个参数是数据表的字段名,第二个参数是对应的修改值。 UPDATE
f_studentsSETgender=false WHEREf_students.idIN (10,11,12) AND (id= 10 ORid= 11 ORid= 12)。支持直接传入若干个结构体进行批量更新,结构体下同样要考虑需不需要处理零值。注意这里的选择方式是 Model指定查询的结果类型是Student结构体,然后指定查询条件 age = 99。新的这个查询条件能够查出来的结果,这种方式不会将结构体对象的零值写入数据行的字段
- 使用
4. 删除数据操作
-
- 使用
DB.Delete(&deletedStudent, []int{7, 9, 11})删除多条记录。
- 使用
总结
在本文中,我们介绍了GORM作为一个强大的对象关系映射(ORM)库,它为Go语言开发者提供了一种简洁、高效的方式来处理数据库操作。通过GORM,我们可以轻松地进行数据库的增删改查操作,而无需直接编写复杂的SQL语句。
GORM不仅仅是一个ORM库,它还提供了许多其他的功能,如数据库迁移、事务管理等。通过数据库迁移功能,我们可以轻松地更新数据库结构,添加新的表或字段,而无需手动编写SQL语句。事务管理功能则确保了数据库操作的原子性和一致性,使得在复杂的业务逻辑中能够准确地处理数据库操作。
一个重要的概念是,GORM将数据库表映射到Go语言的结构体。通过定义结构体,并使用GORM的标签和方法,我们可以指定结构体字段与数据库表的映射关系,从而实现数据的持久化和检索。这种对象关系映射的方式使得开发者可以通过操作结构体来进行数据库操作,而无需直接编写SQL语句,从而提高开发的效率和代码的可维护性。
使用GORM,我们可以在Go项目中快速地进行数据库操作,减少了编写和维护SQL语句的工作量。同时,GORM提供了丰富的查询和操作方法,使得我们可以灵活地处理各种数据库操作需求。无论是简单的单表查询,还是复杂的多表关联查询,GORM都能提供便捷的解决方案。
总之,通过使用GORM,我们可以以一种简洁、高效、易于理解的方式与数据库进行交互。它简化了数据库操作的过程,提供了丰富的功能和灵活的查询方式。对于Go语言开发者来说,GORM是一个强大的工具,可以帮助我们快速地开发数据库相关的功能,提高开发效率和代码质量。