简单使用gin+gorm实现CRUD

624 阅读6分钟

参考了www.bilibili.com/video/BV1aB…

1.项目目标

我们的目标是通过gin+gorm实现数据库的增删改查,也就是CRUD(create,read,update,delete),并使用postman去检测我们端口的设计成功与否。 该文档的目的旨在将项目构建的方式及其思考方式全程搭建起来。 tips:随时查看gin和gorm的文档,有清晰且完整的中文教程。

2.项目思路

1.该项目是在goland本地运行,故必须先满足依赖需求,如mysql以及数据库操作软件,我这里使用的是DBeaver去检查和操作数据库情况。 2.该项目是由gin+gorm编写的,建议提前下好库以及gorm关于mysql的依赖。且记得提前创建数据库表。 3.创建web服务器,再进行绑定数据库db。 4.使用RESTful模式去编写端口,设置post,delete,put,get几个方法,并一步一步的去实现他。

3.项目过程

1.创建webapp

所有的事情都从这里开始,我们首先创建web服务器,使用gin.Default()我们可以直接定义web服务器,再通过r.Run()进行运行。

package main
​
import (
    "fmt"
    "net/http"
    "strconv"
    "time"
​
    "gorm.io/gorm/schema"
​
    "github.com/gin-gonic/gin"
    "gorm.io/driver/mysql"
    "gorm.io/gorm"
)
func main() {
    r := gin.Default()
    port := "3113"
    r.Run(":" + port) // 运行webapp
}
​

2.连接数据库

在进行数据库的连接前,我们得进行Model的创建。一般推荐先进行Model的创建,这样使用起来更方便。其中Model里设置传入json的key。

type List struct {
    gorm.Model        //id,created_at,updated_at
    Name       string `json:"Name"`
    Status     string `json:"Status"`
    Phone      string `json:"Phone"`
    Email      string `json:"Email"`
    Address    string `json:"Address"`
}

接下来在main函数里进行数据库的连接

    // Connect DateBase
    dsn := "root:Csd123456,@tcp(127.0.0.1:3306)/crud_sample?charset=utf8mb4&parseTime=True&loc=Local"
    db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{
        NamingStrategy: schema.NamingStrategy{ // 把单数表创建开启,即表创建出来是list而不是lists
            SingularTable: true,
        },
    })
    fmt.Println(db)
    fmt.Println(err)
​
    // 以下代码复制于https://gorm.io/zh_CN/docs/connecting_to_the_database.html
    sqlDB, err := db.DB()
​
    // SetMaxIdleConns 设置空闲连接池中连接的最大数量
    sqlDB.SetMaxIdleConns(10)
​
    // SetMaxOpenConns 设置打开数据库连接的最大数量。
    sqlDB.SetMaxOpenConns(100)
​
    // SetConnMaxLifetime 设置了连接可复用的最大时间。
    sqlDB.SetConnMaxLifetime(10 * time.Second) // 十秒钟// 绑定数据库
    db.AutoMigrate(&List{})

以下是重中之重,其中password是你数据库的密码,而crud_sample是你刚创建的数据库。

    dsn := "root:password@tcp(127.0.0.1:3306)/crud_sample?charset=utf8mb4&parseTime=True&loc=Local"

此时我们运行程序,打开数据库软件,会发现出现表已经创建。 image.png 接下来我们在main函数进行测试

    // no route will run
    r.NoRoute(func(c *gin.Context) {
        // 实现内部重定向
        c.HTML(http.StatusOK, "404.html", gin.H{
            "title": "404",
        })
    })
    // test
    r.GET("/test", func(c *gin.Context) {
        c.JSON(200, "success")
    })
​

接下来我们使用postman发送get请求到127.0.0.1:3113/test 显示success,证明我们的路由也没问题。 image.png ####

3.进行GET方式的实现

实现get方法的思路:

1.使用ShouldBindJSON函数将传来的参数传入data之中。 2.写好错误解释方式 ,当返回无错误时,使用db.Create()去创建data数据。 3.返回json数据用以显示成功。

    // add
    r.POST("/usr/add", func(c *gin.Context) {
        var data List
        err := c.ShouldBindJSON(&data)
        if err != nil {
            c.JSON(http.StatusOK, gin.H{
                "message": err.Error(),
                "data":    gin.H{},
                "code":    "400",
            })
        } else {
            db.Create(&data) //create a line in the database
            c.JSON(200, gin.H{
                "message": "success",
                "data":    data,
                "code":    http.StatusOK,
            })
        }
    })

在postman进行检测,使用post访问127.0.0.1:3113/usr/add。 进行body的编写,用以传入数据。 image.png 此时我们能在我们数据库软件中查看到我们新增的数据。

4.进行DELETE方式的实现

实现delete的思路

1.在delete中我们通过对id的访问去删除数据,所以我们先要使用c.Param获取url所给的id,即是:id。 2.使用db.Where("id=?", id).Find(&data)去访问数据库中所符合的id。 3.再排除无该id的索引后,我们使用Delete方法去实现该条data的删除。,并使用c.JSON返回成功信息。

    // delete
    r.DELETE("/usr/delete/:id", func(c *gin.Context) {
        var data []List
​
        // accept id
        id := c.Param("id")
​
        db.Where("id=?", id).Find(&data)
        if len(data) == 0 { // if not found id
            c.JSON(200, gin.H{
                "message": "delete failed,not found id",
                "code":    "400",
            })
        } else {
            db.Where("id=?", id).Delete(&data)
            c.JSON(200, gin.H{
                "message": "delete succeeded",
                "code":    "200",
            })
        }
    })

打开postman,找到delete方法,在url输入127.0.0.1:3113/usr/delete/1,我们就删除刚创建的数据。 image.png 但由于我们orm使用的是软删除,其可能仍能在数据库中显示,但其列表中显示了删除时间。

5.进行PUT方式的实现

我们先使用POST请求多创建几条数据方便我们PUT和GET。

实现put的思路

1.我们延续我们DELETE中的思路通过c.Param去接收id号。 2.在排除错误情况后,我们使用ShouldBindJSON将接受的body传入data中。 3.我们通过db.where方法查询id所在数据,并使用Update方法去将传入的数据体中。 4.返回显示成功的json

    // put
    r.PUT("/usr/update/:id", func(c *gin.Context) {
        var data List
​
        // accept id
        id := c.Param("id")
        db.Select("id").Where("id=?", id).Find(&data)
        if data.ID == 0 { // if not found id
            c.JSON(200, gin.H{
                "message": "Put failed,not found id",
                "code":    "400",
            })
        } else { // ID is already
            err := c.ShouldBindJSON(&data)
            if err != nil {
                // put failed
                c.JSON(200, gin.H{
                    "message": "put failed",
                    "code":    "400",
                })
            } else {
                db.Where("id = ?", "id").Updates(&data)
                c.JSON(http.StatusOK, gin.H{
                    "message": "put succeeded",
                    "code":    "200",
                })
            }
        }
    })

让我们打开postman来检测一下。创建新的request,设置method为PUT,url为127.0.0.1:3113/usr/update/2 输入自己手写的body,打开数据库我们会发现我们第二条数据已被修改。 image.png

5.进行GET方式的实现-1

为什么要将GET方式分两种讨论,因为这就涉及我们会有请求单条数据或查看很多数据的两种方式。我们首先实现的是请求单条数据。

实现get的思路-1

1.我们首先想到的是使用name来请求数据的显示,也就是zhangsan,lisi,wangwu这种。延续之前的思路使用c.Param去接收数据。 2.我们也和之前一样使用db.Where("name=?", name).Find(&data)去寻找数据,并用Find绑定到data上。 3.排除错误,并返回查询的数据。

    // get// use name to get conditions
    r.GET("/usr/list/:name", func(c *gin.Context) {
        name := c.Param("name")
        var data []List // find datalists
        db.Where("name=?", name).Find(&data)
        if len(data) == 0 {
            c.JSON(200, gin.H{
                "message": "not found",
                "code":    "400",
                "data":    gin.H{},
            })
        } else {
            c.JSON(200, gin.H{
                "message": "get successfully",
                "code":    "200",
                "data":    data,
            })
        }
    })
​

接下来我们仍然打开post进行测试。找到GET方法,输入127.0.0.1:3113/usr/list/wangwu,来查询我们在之前输入的数据。

6.进行GET方式的实现-2

接下来我们实现按页查询或查询所有数据.

实现get的思路-2

1.我们首先实现是将所有的数据进行展示db.Model(datalist).Count(&total).Limit(-1).Offset(-1).Find(&datalist),通过将Limit和Offset设置为-1,我们访问了数据库中所有的数据并传入在datalist中。 2.随后我们就像第一个get请求一样进行数据的表出。 3.我们此时又想到我们得进行分页管理,便设置了pageSize为页面大小,显示几条数据,pageNum是第几个页面。 4.将Limit参数和Offset参数进行更改,得到如下的代码。 5.由于pageSize和pageNum默认为0,设置pageSize和pageNum为0时为-1,这将展示所有的数据。

    // get all or get by page
    r.GET("/usr/list", func(c *gin.Context) {
        var datalist []List
        pageSize, _ := strconv.Atoi(c.Query("pageSize")) //use strconv.Atoi here to make string to int
        pageNum, _ := strconv.Atoi(c.Query("pageNum"))
​
        if pageSize == 0 { // if no pageSize or pageNum, show all page
            pageSize = -1
        }
        if pageNum == 0 {
            pageNum = -1
        }
​
        // get all data
        var total int64
        db.Model(datalist).Count(&total).Limit(pageSize).Offset(pageNum).Find(&datalist)
        if len(datalist) == 0 {
            c.JSON(200, gin.H{
                "message": "get failed,no data",
                "code":    "400",
                "data":    gin.H{},
            })
        } else {
            c.JSON(200, gin.H{ //return datalist json response
                "message": "get succeed",
                "code":    "200",
                "data": gin.H{
                    "list":     datalist,
                    "total":    total,
                    "pageNum":  pageNum,
                    "pageSize": pageSize,
                },
            })
        }
    })

打开postman,url输入127.0.0.1:3113/usr/list进行检测。 我们可以在postman的Params中设置参数,传入pageNum和pageSize,其会自动修改请求来达到你的要求。 当我们send后会在下面显示你所想展示的数据。 image.png

以上,所有的功能实现完成。