青训营X豆包MarsCode 技术训练营:使用GORM实现数据库的增删改查 | 豆包MarsCode AI刷题

136 阅读8分钟

1.安装GORM

  • 将GO语言的环境变量GOPATH配置好,之后下载的库都会下载到GOPATH这里,可以在GOPATH这个环境变量下的路径的mod下找到 在终端执行这两行命令

go get -u gorm.io/gorm go get -u gorm.io/driver/mysql

2.创建一个mysql数据库

  1. 终端打开mysql window下可以打开powershell 然后输入 mysql -u root -p 然后输入密码就可以登入mysql数据库
  2. 创建一个新的数据库 godb CREATE DATABASE godb

GORM学习-20241124182636806.webp

GORM学习-20241124183438030.webp

3. 使用GORM连接数据库

  1. 在go文件中创建一个产品结构体 Product
type Product struct {
  gorm.Model
  Code string
  Price string
}

var p Product
var products []Product

看到gorm.Model中的结构是

type Model struct {
    ID        uint `gorm:"primarykey"`
    CreatedAt time.Time
    UpdatedAt time.Time
    DeletedAt DeletedAt `gorm:"index"`
}

可以看到其中有创建时间和更新时间和删除标记 2. 连接数据库 1.定义dsnMySQL 数据库连接字符串,数据库的端口,连接数据库名,charset字段设置为utf8m64支持表情符号等字符, parseTime=True表示将数据库中的日期/时间字段(如 DATETIME 或 TIMESTAMP)自动解析为 Go 的 time.Time 类型 loc=Local:指定时区为本地时区

dsn := "username:password@tcp(127.0.0.1:3306)/godb?charset=utf8mb4&parseTime=True&loc=Local"
  1. 然后连接数据库,使用db.AutoMigrate(&Product{}),会根据结构体的字段和标签来判断如何创建或更新数据库中的表。如果表不存在,GORM 会创建它;如果表已经存在,GORM 会尝试根据结构体的字段修改表的结构(比如添加新的字段、修改字段类型等)。
db,err := gorm.Open(mysql.Open(dsn),&gorm.Config{})
  if err != nil{
    fmt.Println("gorm.Open failed",err)
    panic(err)
  }
  db.AutoMigrate(&Product{})

可以看到这里已经创建了表,并且结构也是我们定义的

GORM学习-20241125175019587.webp

4. 使用GORM对数据库增删改查

使用GORM库创建
1. 定义一个表,然后创建表
   p1 := &Product{
   Code: "C23",
   Price: 100 ,
  }
  db.Create(p1)

GORM学习-20241125175321389.webp

创建一个指向 Product 结构体实例的指针切片,使用Create() 创建多项记录。使用指针 (*Product) 可以避免拷贝结构体,且允许修改原始数据。

  product := []*Product{
	{Code: "A12", Price: 20},
    {Code: "B34", Price: 50},
    {Code: "C12", Price: 20},
    {Code: "D34", Price: 50},
}
  db.Create(product)

GORM学习-20241125175606667.webp

2. 使用GORM库进行查询操作
1. 使用First()根据主键检索

按主键查询,使用First()用来查询数据库中指定条件匹配的第一条记录,这里指定主键为1. First(dest interface{}, conds ...interface{})第一个值传入结构体指针,查询后的值会传入到这个结构体中,第二个值传入

  var p Product
  db.First(&p,1)
  fmt.Printf("p:%#v\n",p)

输出结果如下:

p:main.Product{Model:gorm.Model{ID:0x1, CreatedAt:time.Date(2024, time.November, 25, 17, 51, 55, 859000000, time.Local), UpdatedAt:time.Date(2024, time.November, 25, 17, 51, 55, 859000000, time.Local), DeletedAt:gorm.DeletedAt{Time:time.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC), Valid:false}}, Code:"C23", Price:0x64}

如果主键是字符串(例如像uuid),查询将被写成如下: db.First(&user, "id = ?", "1b74413f-f3b8-409f-ac47-e8c062e3472a")

2. 使用Find()检索

直接使用db.Find()的话,相当于在mysql中使用SELECT * FROM users;

定义一个变量products为结构体切片,然后使用db.Find(&products),查询表中全部元素,会返回一个(tx *gorm.DB)对象

var products []Product
result := db.Find(&products)
fmt.Printf("len:%#v\n",result.RowsAffected)
fmt.Printf("err:%#v\n",result.Error)

result.RowsAffected 的值是表中数据的数量 result.Error 返回是否有错误 返回如下结果

len:5 err:<nil>

3. 使用Find()检索带条件

使用Where可以选择条件进行查询 db.Where("Price=?",20).Find(&products) 使用这个语句返回表中查询到的所有Price等于20的数据

3. 使用GORM库进行更新操作
1. 使用db.Model(&product).Update()改写已存在的数据

在Update()写入需要更新的数据的键值对,在Model中传入需要改动的结构体指针

  var p []Product
  db.First(&p,3)
  fmt.Printf("p:%#v\n",p)
  db.Model(&p).Update("Price",200)
  fmt.Printf("p:%#v\n",p)

p:[]main.Product{main.Product{Model:gorm.Model{ID:0x3, CreatedAt:time.Date(2024, time.November, 25, 17, 55, 56, 769000000, time.Local), UpdatedAt:time.Date(2024, time.November, 25, 17, 55, 56, 769000000, time.Local), DeletedAt:gorm.DeletedAt{Time:time.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC), Valid:false}}, Code:"B34", Price:0x32}}

p:[]main.Product{main.Product{Model:gorm.Model{ID:0x3, CreatedAt:time.Date(2024, time.November, 25, 17, 55, 56, 769000000, time.Local), UpdatedAt:time.Date(2024, time.November, 25, 21, 12, 53, 457000000, time.Local), DeletedAt:gorm.DeletedAt{Time:time.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC), Valid:false}}, Code:"B34", Price:0xc8}}

可以看到Price已经被改动了,从原来的50被改成200了

2. 使用db.Model(&product).Updates() 更新多个字段

Updates() 方法可以同时更新多个字段。只要你在传入的结构体或 map 中指定了多个字段,它会一并更新

 var p []Product
  db.First(&p,5)
  fmt.Printf("p:%#v\n",p)
  db.Model(&p).Updates(Product{Code:"v78",Price: 700})
  fmt.Printf("p:%#v\n",p)

我们可以看到一下输出信息,Code和Price已经被更新了

p:[]main.Product{main.Product{Model:gorm.Model{ID:0x5, CreatedAt:time.Date(2024, time.November, 25, 18, 7, 56, 364000000, time.Local), UpdatedAt:time.Date(2024, time.November, 25, 18, 7, 56, 364000000, time.Local), DeletedAt:gorm.DeletedAt{Time:time.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC), Valid:false}}, Code:"D34", Price:0x32}}

p:[]main.Product{main.Product{Model:gorm.Model{ID:0x5, CreatedAt:time.Date(2024, time.November, 25, 18, 7, 56, 364000000, time.Local), UpdatedAt:time.Date(2024, time.November, 25, 21, 23, 44, 72000000, time.Local), DeletedAt:gorm.DeletedAt{Time:time.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC), Valid:false}}, Code:"v78", Price:0x2bc}}

4. 使用GORM库进行删除操作

删除一条记录时,删除对象需要指定主键,否则会触发批量删除

1. 软删除

如果表中包含 gorm.DeletedAt字段(该字段也被包含在gorm.Model中),那么该模型将会自动获得软删除的能力!

当调用Delete时,GORM并不会从数据库中删除该记录,而是将该记录的DeleteAt设置为当前时间,而后的一般查询方法将无法查找到此条记录

2.根据主键删除

GORM 允许通过主键(可以是复合主键)和内联条件来删除对象,它可以使用数字,也可以使用字符串

db.Delete(&p)表明删除 p 对象在数据库中对应的记录,我这里选择主键为5的数据,gorm库会自动构建一条sql语句DELETE FROM products WHERE id = 5;来删除主键为5的记录

  var p Product
  db.First(&p,5)
  fmt.Printf("p:%#v\n",p)
  db.Delete(&p,5)
  fmt.Printf("p:%#v\n",p)

p:main.Product{Model:gorm.Model{ID:0x5, CreatedAt:time.Date(2024, time.November, 25, 18, 7, 56, 364000000, time.Local), UpdatedAt:time.Date(2024, time.November, 25, 21, 23, 44, 72000000, time.Local), DeletedAt:gorm.DeletedAt{Time:time.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC), Valid:false}}, Code:"v78", Price:0x2bc}

p:main.Product{Model:gorm.Model{ID:0x5, CreatedAt:time.Date(2024, time.November, 25, 18, 7, 56, 364000000, time.Local), UpdatedAt:time.Date(2024, time.November, 25, 21, 23, 44, 72000000, time.Local),DeletedAt:gorm.DeletedAt{Time:time.Date(2024, time.November, 25, 21, 46, 22, 969000000, time.Local), Valid:true}}, Code:"v78", Price:0x2bc}

我们可以看到数据已经被软删除了,DeletedAt的值已经变成修改时间了

这时候再调用查询 db.First(&p,5),会显示record not found

3.查找被软删除的记录

可以使用Unscoped来查询到被软删除的记录

db.Unscoped().Where("id=5").Find(&users)

  db.Unscoped().Where("id=5").Find(&products)
  fmt.Printf("p:%#v\n",products)

我们可以看到被软删除的id为5的数据被查询到了

p:[]main.Product{main.Product{Model:gorm.Model{ID:0x5, CreatedAt:time.Date(2024, time.November, 25, 18, 7, 56, 364000000, time.Local), UpdatedAt:time.Date(2024, time.November, 25, 21, 23, 44, 72000000, time.Local), DeletedAt:gorm.DeletedAt{Time:time.Date(2024, time.November, 25, 21, 46, 22, 969000000, time.Local), Valid:true}}, Code:"v78", Price:0x2bc}}

4.永久删除记录

可以使用 Unscoped()来永久删除匹配的记录

下面的First语句会返回record not found 但是后面的永久删除语句生效了

  var p Product
  db.First(&p,5)
  db.Unscoped().Where("id=5").Delete(&p)

使用查询函数


db.Unscoped().Where("id=5").Find(&p)
  fmt.Printf("p:%#v\n",p)

返回以下信息,我们可以看到值全部为0,意味着我们查询不到这个id为5的数据了,它已经被我们永久删除了

[1.139ms] [rows:0] SELECT * FROM products WHERE products.id = 5 AND products.deleted_at IS NULL ORDER BY products.id LIMIT 1 p:main.Product{Model:gorm.Model{ID:0x0, CreatedAt:time.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC), UpdatedAt:time.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC), DeletedAt:gorm.DeletedAt{Time:time.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC), Valid:false}}, Code:"", Price:0x0}

5.总结与思考

  1. GORM 提供了简洁的 API,将复杂的 SQL 操作封装为直观的函数调用,方便我们开发者使用,可以不用写冗余的SQL语句来操作数据库的数据。这种设计对不熟悉SQL语句的新手来说是非常友好,非常容易上手,提高开发效率,但隐藏了底层的 SQL 操作细节,如果想要深入了解数据库仍需要学习下SQL语句。
  2. GORM有非常丰富详细的文档,可供开发者参考,开发者可以根据文档快速上手,进行非常快捷便利的学习使用GORM库,降低了搜索使用方法的学习成本。 3.GORM 支持自动迁移功能(AutoMigrate),可以根据定义的结构体模型自动创建或更新数据库表。 这种特性让开发者不需要手动编写表结构的创建和修改语句,提高开发效率提供便利。 这种设计对快速开发项目和实现原型非常有帮助,但过度依赖可能导致开发者忽略对数据库结构的优化,可能会引发冗余字段或表设计不规范的问题。建议开发者在开发中使用GORM库的同时也不要忘了结合手动 SQL 脚本管理数据库结构,以确保更好的性能和稳定性