使用 GORM(Go 的 ORM 库)连接数据库,并实现增删改查操作 |青训营

79 阅读6分钟

准备工作:

电脑上部署一款数据库系统如Mysql或Sql Server(大一教学用的了属于是),GORM 官方支持的数据库类型有:MySQL, PostgreSQL, SQLite, SQL Server 和 TiDB.

这里以Mysql为例(总体流程是一样的)配置mysql可以参照这篇,需要记好自己的用户密码,后续通过go连接需要使用到注册使用的用户名和密码,需要记忆。

2023 年 MySQL 8.0 安装配置 最简易(保姆级)_mysql8.0安装配置教程_mobeicanyue的博客-CSDN博客

下载完成后运行,可以去任务资源管理器的服务再次确认是否已经启动MySQL服务。

如果实在不想去下载mysql,可以使用一下命令快速运行一个MySQL8.0.19实例,当然前提是你要有docker环境…

在本地的13306端口运行一个名为mysql,root用户名密码为root的MySQL容器环境:

docker run --name mysql -p 13306:3306 -e MYSQL_ROOT_PASSWORD=root1234 -d mysql:8.0.19

在另外启动一个MySQL Client连接上面的MySQL环境,密码为上一步指定的密码root1234:

docker run -it --network host --rm mysql mysql -h127.0.0.1 -P13306 --default-character-set=utf8mb4 -uroot -p

可参考于GORM入门指南 | 李文周的博客 (liwenzhou.com)

基本认知:

GORM (Go Object Relational Mapping)是一个流行的 Go 语言 ORM(对象关系映射)库,用于简化在 Go 应用程序中与关系型数据库的交互。它提供了一个简洁、易用且功能强大的 API,帮助开发者更便捷地进行数据库操作。

模块安装:

使用GORM需要安装对应的模块,如果此前已经设置好了代理的GOPROXY路径,引入之后快速修复即可通过自动引入模块。 对于 GORM v1,你需要使用以下导入路径:

import (
	"github.com/jinzhu/gorm"
	_ "github.com/jinzhu/gorm/dialects/mysql"
)

而对于 GORM v2,你需要使用以下导入路径:

import (
	"gorm.io/gorm"
	"gorm.io/driver/mysql"
)

我们可能会在一些较早的教程和实例中看到引入GORMv1的例子。 GORM v1 和 GORM v2 之间存在一些重大的更改和差异。如果正在开始一个新的项目或准备进行升级,请考虑使用 GORM v2,因为它是 GORM 最新的稳定版本,并且支持更多功能和改进。 也可以在powershell中用命令行:

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

对象构建

为了之后的增删改查工作更加具体,我们需要定义一个表示用户对象的结构体。

type User struct {
    gorm.Model
    Name     string
    Age      int
    Birthday time.Time
}

这里需要提及一下 gorm.Model,它包含了以下字段:

  • ID uint:用于存储用户的唯一标识,作为主键。
  • CreatedAt time.Time:记录创建的时间戳。
  • UpdatedAt time.Time:记录更新的时间戳。
  • DeletedAt gorm.DeletedAt:记录删除的时间戳。当记录删除时,该字段将被设置为非零值(通常是 NULL)。 当创建和更新记录时对应字段则会转为对应的时间戳保存下来,当删除记录时,GORM 会自动将 DeletedAt 字段设置为当前的时间戳,而不是直接从数据库中删除该记录(软删除)。

定义主函数:

首先要与数据库进行连接 我们使用gorm.open方法来建立与 MySQL 数据库的连接

func main() {
  dsn := "user:pass@tcp(127.0.0.1:3306)/dbname?charset=utf8mb4&parseTime=True&loc=Local"
  db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
}

以上是最新GORM官方文档对连接Mysql的的示例,较早的版本使用字符串定位略有不同

  • 如果不清楚自己的用户名,可以通过以下方式查询:
SELECT USER();

在MySql中初始用户名一般默认为root。

  • 查询当前登录用户的主机名:
SELECT @@hostname;

日常不会使用到,直接选择127.0.0.1:3306,对应即是Mysql所占用本地端口,不用变更,

	if err != nil {//连接失败就抛出异常,
		panic("failed to connect database")
	}

在进行行连接数据库之前,需要定义一个可操作的数据库实例即 dsn := "user:pass@tcp(127.0.0.1:3306)/dbname?charset=utf8mb4&parseTime=True&loc=Local"中对应的dbname,我们打开MySQL使用如下语句创建即可(这里笔者忘记语法直接回车,分号加在第二行):

CREATE DATABASE db

image.png

错误处理

使用前面定义的err进行错误处理的定义

	if err != nil {//连接失败就抛出异常,
		panic("failed to connect database")
	}

数据操作

数据操作的更多语法在GORM官方文档中有详细的解释 (gorm.io/zh_CN/docs/…) 本文亦参照其进行增删改查的简单操作演示

迁移(自动创建表)

db.AutoMigrate(&User{})

GORM提供的方法,让 GORM 自动检测 User 模型的定义,并将其映射到相应的数据库表。如果数据库中不存在表,它会创建一个新表。如果表已存在,它会自动添加缺失的列或对现有列进行修改。在我的演示实例中就是创建一个新表。

添加操作

我们需要定义一个对象,通过指针的方式将该对象的值写入对应字段中。

user1 := User{Name: "Jinzhu", Age: 18, Birthday: time.Now()}
user2 := User{Name: "Jackson", Age: 19, Birthday: time.Now()}

//users := []*User{     User{Name: "Jinzhu", Age: 18, Birthday: time.Now()},     //User{Name: "Jackson", Age: 19, Birthday: time.Now()}, }
//以上为创建多行记录的方法,但不适合用于增删改查的演示,科普

	 // 创建记录
	db.Create(&user1)
	db.Create(&user2)

如果需要将user的所有属性值都写入,使用db.Create(&user)。 如果只选取一部分:db.Select("Name", "Age", "CreatedAt").Create(&user)

查询操作

在创建完两条记录之后,我们通过不同方式的查询来搜索记录。

  1. 查询单个记录:

    var u = new(User)
    db.First(u)
        //db.Last(u)// 获取最后一条记录(主键降序)
        //db.take(u)// 获取一条记录,没有指定排序字段
    fmt.Printf("%#v\n", u)
    
  2. 根据条件查询单个记录:

    var user User
    db.Where("age = ?", 18).First(&user) // 根据年龄为 18 的条件查询第一个匹配的记录
    

如果运行正确,结果会是返回user1和user2的记录,如下图

image.png 可见在自动生成的Model之后,user1和user2的记录准确被查询到。

更新操作

对指针uu所指向的数据(年龄为19的user记录)进行更新操作,我们可以作一个前后对比

var uu User
	db.Find(&uu, "Age=?", "19")
	fmt.Printf("%#v\n", uu)
	//更新
	db.Model(&uu).Update("Age", "20")
	fmt.Printf("%#v\n", uu)

更新成功,则应当分别打印出除了UppdatedAT和Age不同,其他相同的user2信息。

image.png

删除操作

var uu User
	db.Find(&uu, "Age=?", "19")
	fmt.Printf("%#v\n", uu)
        	// 删除
	db.Delete(&uu)

执行删除操作后,将恢复为默认值,该记录将不存在,执行结果如下:

image.png

总结

一开始走了很多弯路,以至于放弃,但是总算能寻到方法,实现这一操作,轻松很多,对于GORM尚不能明白其及具体使用场景,希望能日后精进。