前言
GORM是Go语言中应用广泛的对象关系映射(ORM)库,凭借其简洁、功能丰富的接口设计,极大地方便了开发者与数据库的交互。传统上,在操作数据库时,开发者需要编写大量SQL语句,不仅容易导致代码冗长,而且在不同数据库之间难以迁移,存在大量代码需要重写。而GORM通过封装数据库操作,允许开发者直接调用GORM提供的接口完成数据库操作,而无需编写SQL语句。这不仅提升了代码的可读性,降低了产生Bug的风险,还增强了代码的可维护性与可扩展性。
本文将以MySQL数据库为例,详细介绍如何使用GORM进行数据库连接设置、数据模型创建以及增删改查操作。通过具体示例,展示如何定义结构体作为数据模型,建立模型与数据库表的映射关系,并进行基本的CRUD(Create、Read、Update、Delete)操作,使开发者能够轻松上手GORM在实际项目中的应用。希望通过本文的讲解,能够帮助读者快速上手GORM的核心功能。
1. 初始化项目
1.1 项目结构
为了方便我们接下来的代码编写,我们按照以下的格式创建一个Go项目:
gorm-demo/
├── main.go
├── models/
│ └── user.go
├── go.mod
其中,main.go是程序的入口,models/user.go用于定义数据模型。
1.2 安装GORM与数据库驱动
在使用GORM之前,首先需要使用Go的包管理器安装GORM和数据库驱动。本文以MySQL数据库为例,安装命令如下:
go mod init gorm-demo
go get -u gorm.io/gorm
go get -u gorm.io/driver/mysql
2. 连接数据库
要操作数据库,首先需要连接到数据库。我们首先在main.go中配置数据库连接。假设我们要连接到的MySQL数据库名称为gorm_demo,用户名为root,密码为123456,则连接代码:
package main
import (
"gorm.io/driver/mysql"
"gorm.io/gorm"
"log"
)
var DB *gorm.DB
func initDB() {
db_str := "root:password@tcp(127.0.0.1:3306)/gorm_demo?charset=utf8mb4&parseTime=True&loc=Local"
db, err := gorm.Open(mysql.Open(db_str), &gorm.Config{})
if err != nil {
log.Fatalf("Connect Database Failed: %v", err)
}
DB = db
}
func main() {
initDB()
}
在上面的initDB()方法中,我们首先定义了db_str这个字符串,它是数据库的连接字符串,用于给出数据库的地址、用户名、密码等连接相关的信息。然后,我们使用gorm.Open()方法,通过db_str创建数据库连接实例,并保存到DB变量。
3. 定义数据模型
在GORM中,数据模型用于描述数据库中表的定义,同时用于映射数据库中的数据到Go中的结构体对象。假设gorm_demo数据库中有一个users表,定义如下:
CREATE TABLE users (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`created_at` datetime(3) DEFAULT NULL,
`updated_at` datetime(3) DEFAULT NULL,
`deleted_at` datetime(3) DEFAULT NULL,
`name` varchar(255) NOT NULL,
`email` varchar(255) DEFAULT NULL,
`age` int DEFAULT NULL,
PRIMARY KEY (`id`)
);
我们在models/user.go中创建如下的结构体:
type User struct {
gorm.Model
Name string `gorm:"unique"`
Email string
Age int
}
func (User) TableName() string {
return "users"
}
在上方的结构体中,gorm.Model是GORM所提供的基础数据模型,包括了ID、创建/修改/删除时间字段,并由GORM在操作数据库时自动生成。gorm:"unique"标签代表该属性在数据库中所对应的字段存在唯一约束。在此处我们通过实现GORM中的Tabler接口,手动指定表名为users。如果在数据库中不存在该表,需要创建,GORM为我们提供了自动迁移功能,能够把Go中的结构体对应的表结构创建到数据库中。
DB.AutoMigrate(&User{})
4. 操作数据库
4.1 插入数据
如下面的createUser()方法所示,我们可以使用Create()方法向数据库中插入新数据。
func createUser(name, email string, age int) {
user := models.User{Name: name, Email: email, Age: age}
result := DB.Create(&user)
if result.Error != nil {
fmt.Printf("createUser Error: %v\n", result.Error)
} else {
fmt.Printf("createUser Success: %v\n", user.ID)
}
}
在createUser()方法中,我们创建了一个User类型的实例,填充了数据,并调用DB.Create()方法将其插入到数据库中。在插入成功后,GORM会把数据库自动生成的主键填充到我们创建的实例当中,便于在后续代码中使用。
4.2 查询数据
GORM 提供了多种灵活的数据查询接口,能够满足开发者在各类数据查询场景中的需求。例如,Take、First、Last和Find方法可用于从数据库中获取单条或多条记录,满足不同情境下的数据读取需求。Take方法通常用于获取随机记录,First和Last方法则分别用于获取结果集中的首条和末条记录,而Find方法则能够获取符合条件的所有记录。
在查询条件方面,GORM 提供了强大的条件设置功能。通过使用Where方法,我们可以像在SQL中一样指定各种查询条件;Not和Or方法能够提升组合查询条件的可读性,便于实现复杂的查询筛选逻辑。
此外,GORM 还提供了一些控制查询结果输出的接口。例如,Select方法可以选择性地返回查询结果中的某些字段,而不是返回所有字段,这在只需要部分数据时可以有效提升查询性能;Order方法则可以用于对查询结果进行排序,无论是按升序还是降序排列。
4.2.1 一般查询
Take、First和Last方法用于查询单条数据。其中,First方法返回按主键升序排序的第一条数据,而Last返回排序后的最后一条数据。如果没有查询到数据,则返回ErrRecordNotFound错误。
var user User
// SELECT * FROM users ORDER BY id LIMIT 1;
DB.First(&user)
//SELECT * FROM users LIMIT 1;
DB.Take(&user)
// SELECT * FROM users ORDER BY id DESC LIMIT 1;
results := DB.Last(&user)
// 返回的数据条数
fmt.Println(result.RowsAffected)
// 返回的error
fmt.Println(result.Error)
// 输出是否没有查询到数据
fmt.Println(errors.Is(result.Error, gorm.ErrRecordNotFound))
而Find方法返回所有数据,因此它接收一个数组参数。
var users []User
// SELECT * FROM users
results := DB.Find(&users)
4.2.2 条件查询
Where方法既提供与SQL语法类似的条件查询,也提供通过结构体参数指定条件的条件查询。
var user User
// SELECT * FROM users WHERE name = 'Tom' AND age > 18 LIMIT 1
DB.Where("name = ? AND age > ?", "Tom", 18).Take(&user)
// SELECT * FROM users WHERE name = 'Tom' AND age = 18 LIMIT 1
DB.Where(&User{Name: "Tom", Age: 18}).Take(&user)
Not和Or方法分别用于指定NOT条件与Or条件,使用方法与Where类似。
var user User
// SELECT * FROM users WHERE NOT name = 'Tom' LIMIT 1
DB.Not("name = ?", "Tom").Take(&user)
// SELECT * FROM users WHERE name <> 'Tom' AND age <> 18 LIMIT 1
DB.Not(&User{Name: "Tom", Age: 18}).Take(&user)
// SELECT * FROM users WHERE name = 'Tom' OR name = 'Bob' LIMIT 1
DB.Where("name = ?", "Tom").Or("name = ?", "Bob").Take(&user)
// SELECT * FROM users WHERE name = 'Tom' OR (name = 'Bob' AND age = 18) LIMIT 1
DB.Where("name = ?", "Tom").Or(&User{Name: "Bob", Age: 18}).Take(&user)
4.2.3 筛选查询结果
在默认情况下,GORM返回表中的所有字段,而Select方法用于指定需要从数据库获取的字段。
var users []User
// SELECT name, age FROM users
DB.Select("name", "age").Find(&users)
4.2.4 查询结果排序
Order方法用于对查询结果进行排序。
var users []User
// SELECT name, age FROM users ORDER BY age desc, name
DB.Order("age desc, name").Find(&users)
4.3 更新数据
从数据库中取出数据之后,Save方法可以用于更新数据库中的原数据。
var user User
DB.Where("name = ?", "Tom").Take(&user)
user.Name = "Bob"
user.Age = 25
// UPDATE users SET name = 'Bob', age = 25, updated_at = '2024-11-04 22:23:37.12' WHERE id = 1
DB.Save(&user)
而Update方法可以用于批量更新满足条件的数据。
// UPDATE users SET age = 30, updated_at = '2024-11-04 22:25:31.73' WHERE name = 'Tom';
DB.Model(&User{}).Where("name = ?", "Tom").Update("age", 30)
在GORM中,默认会禁止执行没有条件的全局更新,返回ErrMissingWhereClause错误。如果需要执行全局更新,可以通过启用AllowGlobalUpdate设置项进行。
// ErrMissingWhereClause
DB.Model(&User{}).Update("age", 30).Error
// UPDATE users SET age = 30
DB.Session(&gorm.Session{AllowGlobalUpdate: true}).Model(&User{}).Update("age", 30)
4.4 删除数据
在GORM中,可以通过带有主键的对象删除一条数据,也可以通过指定条件进行批量删除。
var user User
DB.First(&user)
// DELETE from users WHERE id = 1
DB.Delete(&user)
// DELETE from users WHERE name = 'Bob'
DB.Where("name = ?", "Bob").Delete(&User{})
同样的,在GORM中,默认会禁止执行没有条件的全局删除,返回ErrMissingWhereClause错误。如果需要执行全局删除,可以通过启用AllowGlobalUpdate设置项进行。
// ErrMissingWhereClause
DB.Delete(&User{}).Error
// DELETE FROM users
DB.Session(&gorm.Session{AllowGlobalUpdate: true}).Delete(&User{})
4.4.1 逻辑删除
在GORM中,如果定义数据模型的结构体中存在gorm.DeletedAt字段(gorm.Model中已经包含了该字段),则GORM将对该数据模型自动执行逻辑删除。当调用Delete方法时,GORM将该记录的DeletedAt设置为当前时间,之后的查询操作将无法查询到该记录。
DB.Delete(&User{ID = 10})
// UPDATE users SET deleted_at="2024-11-04 22:27:01.18" WHERE id = 111
// Batch Delete
DB.Where("age = ?", 20).Delete(&User{})
// UPDATE users SET deleted_at="2024-11-04 22:27:10.92" WHERE age = 20
// SELECT * FROM users WHERE age = 20 AND deleted_at IS NULL;
DB.Where("age = 20").Find(&user)
如果需要查找被逻辑删除的记录,可以通过在查询时嵌套Unscoped方法来实现。同样的,嵌套了该方法的删除操作也将从数据库中彻底删除该记录。
// SELECT * FROM users WHERE age = 20
DB.Unscoped().Where("age = 20").Find(&users)
// DELETE FROM users WHERE id=10
DB.Unscoped.Delete(&User{ID = 10})
5. 总结
本文介绍了使用GORM连接数据库的基本过程,并演示了数据库增删改查操作的具体实现。GORM提供的多种接口让我们可以更方便地操作数据库,而无需手工编写大量SQL语句。在实际项目中,GORM可以大大提升数据库操作的效率和代码的可维护性。