使用GORM完成增删改查 | 青训营

142 阅读4分钟

本篇文章采用go + gin +gorm +mysql 实现基本的数据库增删改查功能

1.首先第一步,我们需要安装框架。

在命令行中执行以下操作: 安装gin

go get -u github.com/gin-gonic/gin

安装gorm框架

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

2.创建项目

创建main.go文件并使用init来初始化项目:

go mod init project

3.在main.go中引入依赖

import ("github.com/gin-gonic/gin"
        "gorm.io/gorm" 
        "gorm.io/driver/sqlite")

4.连接数据库

dsn := "root:12345678@tcp(127.0.0.1:3306)/crud-demo?charset=utf8mb4&parseTime=True&loc=Local"
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})

设置数据库的一些基本连接参数

	sqlDB, err := db.DB()
	// SetMaxIdleConns 设置空闲连接池中连接的最大数量
	sqlDB.SetMaxIdleConns(10)
	// SetMaxOpenConns 设置打开数据库连接的最大数量。
	sqlDB.SetMaxOpenConns(100)
	// SetConnMaxLifetime 设置了连接可复用的最大时间。
	sqlDB.SetConnMaxLifetime(10 * time.Second) // 10 秒钟

创建数据库的结构体

	// 结构体
	type List struct {
		Name    string 
		State   string 
		Phone   string 
		Email   string 
		Address string 
	}

迁移 schema

db.AutoMigrate(&List{})

启动gin服务

r := gin.Default()
// 端口号
PORT := "3000"
r.Run(":" + PORT)

5.出现的小问题

如果按照上面那样设置,会出现两个小问题

5.1创建的表没有主键

解决办法:在创建结构体的时候添加gorm.model

GORM 定义了一个 gorm.Model 结构体,其包括字段 IDCreatedAtUpdatedAtDeletedAt

 gorm.Model 的定义
 type Model struct {  
                ID        uint           `gorm:"primaryKey"`  
                CreatedAt time.Time   
                UpdatedAt time.Time   
                DeletedAt gorm.DeletedAt `gorm:"index"` 
                } 

所以现在List结构体变成了这样:

	// 结构体
	type List struct {
          gorm.Model
		Name    string 
		State   string 
		Phone   string 
		Email   string 
		Address string 
	}

2.创建的表会变成复数形式:lists

解决办法,在db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})的时候添加一段代码,现在的代码为:

	db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{
		NamingStrategy: schema.NamingStrategy{
			// 解决查表的时候会自动添加复数的问题 , 例如 user 变成了 users
			SingularTable: true,
		},
	})

6.完善结构体

	type List struct {
		gorm.Model
		Name    string `gorm:"type:varchar(20); not null" json:"name" binding:"required"`
		State   string `gorm:"type:varchar(20); not null" json:"state" binding:"required"`
		Phone   string `gorm:"type:varchar(20); not null" json:"phone" binding:"required"`
		Email   string `gorm:"type:varchar(40); not null" json:"email" binding:"required"`
		Address string `gorm:"type:varchar(200); not null" json:"address" binding:"required"`
	}

注意点 :

  1. 结构体里面的变量 (Name) 必须是首字符大写

2.gorm 指定类型

3.json 表示json接受的时候的名称

4.binding:"required" 表示必须传入

接下来,可以按照restful编码规范编写增删改查接口了。

7.POST接口

主要就是首先定义一个List类型的变量data,将他绑定josn,之后分别编写添加成功和添加失败的逻辑,其中需要注意的是,添加成功以后还需要使用db.Create(&data) 来向数据库之中创建一条数据。

r.POST("/user/add", func(c *gin.Context) {
		var data List

		err := c.ShouldBindJSON(&data)

		// 判断绑定是否有错误
		if err != nil {
			c.JSON(200, gin.H{
				"msg":  "添加失败",
				"data": gin.H{},
				"code": 400,
			})
		} else {
			// 数据库的操作
			db.Create(&data) // 创建一条数据

			c.JSON(200, gin.H{
				"msg":  "添加成功",
				"data": data,
				"code": 200,
			})
		}

	})

8.DELETE接口

大致的思路如下:

  1. 找到对应的 id 所对应的条目
  2. 判断 id 是否存在
  3. 从数据库中删除
  4. 返回, id 不存在的情况下 代码如下:
r.DELETE("/user/delete/:id", func(c *gin.Context) {
		var data []List

		// 接受 id
		id := c.Param("id")

		// 判断 id 是否存在
		db.Where("id = ?", id).Find(&data)

		// id 存在的情况, 则删除, 不存在则报错
		if len(data) == 0 {
			c.JSON(200, gin.H{
				"msg":  "id没有找到, 删除失败",
				"code": 400,
			})
		} else {
			// 操作数据库删除
			db.Where("id = ?", id).Delete(&data)

			c.JSON(200, gin.H{
				"msg":  "删除成功",
				"code": 200,
			})
		}
	})

9.PUT接口的编写 即“改”的接口的编写

主要的思路如下:

  1. 找到对应的 id 所对应的条目
  2. 判断 id 是否存在
  3. 修改对应条目
  4. 返回 id,没有找到

代码如下:

r.PUT("/user/update/:id", func(c *gin.Context) {
		var data List

		// 接受 id
		id := c.Param("id")

		// 判断 id 是否存在
		db.Select("id").Where("id = ? ", id).Find(&data)

		// 判断 id 是否存在
		if data.ID == 0 {
			c.JSON(200, gin.H{
				"msg":  "用户id没有找到",
				"code": 400,
			})
		} else {
			err := c.ShouldBindJSON(&data)

			if err != nil {
				c.JSON(200, gin.H{
					"msg":  "修改失败",
					"code": 400,
				})
			} else {
				// db 修改数据库内容
				db.Where("id = ?", id).Updates(&data)

				c.JSON(200, gin.H{
					"msg":  "修改成功",
					"code": 200,
				})
			}
		}
	})

10.GET接口的编写

10.1条件查询 按名称查询

其中需要注意的是var dataList []List,需要定义切片类型的变量来接受查询到的结果,因为有可能有名字相同的数据存在。

r.GET("/user/list/:name", func(c *gin.Context) {

		// 获取路径参数
		name := c.Param("name")

		var dataList []List

		// 查询数据库
		db.Where("name = ? ", name).Find(&dataList)

		// 判断是否查询到数据
		if len(dataList) == 0 {
			c.JSON(200, gin.H{
				"msg":  "没有查询到数据",
				"code": 400,
				"data": gin.H{},
			})
		} else {
			c.JSON(200, gin.H{
				"msg":  "查询成功",
				"code": 200,
				"data": dataList,
			})
		}
	})

11.查询全部数据 和分页处理数据

代码如下:

r.GET("/user/list", func(c *gin.Context) {

		var dataList []List

		// 1. 查询全部数据,  查询分页数据
		pageSize, _ := strconv.Atoi(c.Query("pageSize"))
		pageNum, _ := strconv.Atoi(c.Query("pageNum"))

		// 判断是否需要分页
		if pageSize == 0 {
			pageSize = -1
		}
		if pageNum == 0 {
			pageNum = -1
		}

		offsetVal := (pageNum - 1) * pageSize
		if pageNum == -1 && pageSize == -1 {
			offsetVal = -1
		}

		// 返回一个总数
		var total int64
		// 查询数据库
		db.Model(dataList).Count(&total).Limit(pageSize).Offset(offsetVal).Find(&dataList)

		if len(dataList) == 0 {
			c.JSON(200, gin.H{
				"msg":  "没有查询到数据",
				"code": 400,
				"data": gin.H{},
			})
		} else {
			c.JSON(200, gin.H{
				"msg":  "查询成功",
				"code": 200,
				"data": gin.H{
					"list":     dataList,
					"total":    total,
					"pageNum":  pageNum,
					"pageSize": pageSize,
				},
			})
		}

	})

至此,crud全部完成,希望可以帮助到想要使用gorm完成crud功能的同学。

参考

GO + Gin + GORM + MySql 实现最基础的 CRUD 结尾+后续计划_哔哩哔哩_bilibili

GORM官方文档:GORM 指南 | GORM - The fantastic ORM library for Golang, aims to be developer friendly.

Gin官方文档:介绍 | Gin Web Framework (gin-gonic.com)