脚本之家,脚本语言编程技术及教程分享平台!
分类导航

Python|VBS|Ruby|Lua|perl|VBA|Golang|PowerShell|Erlang|autoit|Dos|bat|shell|

服务器之家 - 脚本之家 - Golang - Gin+Gorm实现CRUD的实战

Gin+Gorm实现CRUD的实战

2023-03-17 13:57Conqueror712 lv-3 Golang

本文主要介绍了Gin+Gorm实现CRUD的实战,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

简介:

Q:Gin和Gorm都是干什么的?有什么区别?

A:Gin 和 Gorm 是 Go 编程语言中流行的开源库。但是,它们服务于不同的目的,通常在 web 开发项目中一起使用。

Gin 是一个用于构建 HTTP 服务器的 web 框架。它提供了一个简单易用的 API,用于处理 HTTP 请求和响应、路由、中间件和其他常见的 web 应用程序所需的功能。它以其高性能和简约为特点,提供了轻量级和灵活的解决方案来构建 web 服务器。

Gorm 是 Go 的一个 ORM(对象关系映射)库。它提供了一个简单易用的 API,用于与数据库交互、处理数据库迁移和执行常见的数据库操作,如查询、插入、更新和删除记录。它支持多种数据库后端,包括 MySQL、PostgreSQL、SQLite 等。

总而言之, Gin 是用于处理 HTTP 请求和响应、路由、中间件和其他与网络相关的东西的 web 框架,而 Gorm 则是用于与数据库交互并执行常见数据库操作的 ORM 库。它们通常一起使用,来处理 HTTP 请求/响应并在 web 开发项目中存储或获取数据。

开发环境:

  • Windows 10
  • VSCode

一、Gin

0. 快速入门:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
package main
 
import (
    "encoding/json"
    "net/http"
 
    "github.com/gin-gonic/gin"
    "github.com/thinkerou/favicon"
)
 
// 中间件(拦截器),功能:预处理,登录授权、验证、分页、耗时统计...
// func myHandler() gin.HandlerFunc {
//  return func(ctx *gin.Context) {
//      // 通过自定义中间件,设置的值,在后续处理只要调用了这个中间件的都可以拿到这里的参数
//      ctx.Set("usersesion", "userid-1")
//      ctx.Next()  // 放行
//      ctx.Abort() // 阻止
//  }
// }
 
func main() {
    // 创建一个服务
    ginServer := gin.Default()
    ginServer.Use(favicon.New("./Arctime.ico")) // 这里如果添加了东西然后再运行没有变化,请重启浏览器,浏览器有缓存
 
    // 加载静态页面
    ginServer.LoadHTMLGlob("templates/*") // 一种是全局加载,一种是加载指定的文件
 
    // 加载资源文件
    ginServer.Static("/static", "./static")
 
    // 相应一个页面给前端
 
    ginServer.GET("/index", func(ctx *gin.Context) {
        ctx.HTML(http.StatusOK, "index.html", gin.H{
            "msg": "This data is come from Go background.",
        })
    })
 
    // 能加载静态页面也可以加载测试文件
 
    // 获取请求中的参数
 
    // 传统方式:usl?userid=xxx&username=conqueror712
    // Rustful方式:/user/info/1/conqueror712
 
    // 下面是传统方式的例子
    ginServer.GET("/user/info", func(context *gin.Context) { // 这个格式是固定的
        userid := context.Query("userid")
        username := context.Query("username")
        // 拿到之后返回给前端
        context.JSON(http.StatusOK, gin.H{
            "userid":   userid,
            "username": username,
        })
    })
    // 此时执行代码之后,在浏览器中可以输入http://localhost:8081/user/info?userid=111&username=666
    // 就可以看到返回了JSON格式的数据
 
    // 下面是Rustful方式的例子
    ginServer.GET("/user/info/:userid/:username", func(context *gin.Context) {
        userid := context.Param("userid")
        username := context.Param("username")
        // 还是一样,返回给前端
        context.JSON(http.StatusOK, gin.H{
            "userid":   userid,
            "username": username,
        })
    })
    // 指定代码后,只需要在浏览器中http://localhost:8081/user/info/111/555
    // 就可以看到返回了JSON数据了,非常方便简洁
 
    // 序列化
    // 前端给后端传递JSON
    ginServer.POST("/json", func(ctx *gin.Context) {
        // request.body
        data, _ := ctx.GetRawData()
        var m map[string]interface{} // Go语言中object一般用空接口来表示,可以接收anything
        // 顺带一提,1.18以上,interface可以直接改成any
        _ = json.Unmarshal(data, &m)
        ctx.JSON(http.StatusOK, m)
    })
    // 用apipost或者postman写一段json传到localhost:8081/json里就可以了
    /*
        json示例:
        {
            "name": "Conqueror712",
            "age": 666,
            "address": "Mars"
        }
    */
    // 看到后端的实时响应里面接收到数据就可以了
 
    // 处理表单请求 这些都是支持函数式编程,Go语言特性,可以把函数作为参数传进来
    ginServer.POST("/user/add", func(ctx *gin.Context) {
        username := ctx.PostForm("username")
        password := ctx.PostForm("password")
        ctx.JSON(http.StatusOK, gin.H{
            "msg":      "ok",
            "username": username,
            "password": password,
        })
    })
 
    // 路由
    ginServer.GET("/test", func(ctx *gin.Context) {
        // 重定向 -> 301
        ctx.Redirect(301, "https://conqueror712.gitee.io/conqueror712.gitee.io/")
    })
    // http://localhost:8081/test
 
    // 404
    ginServer.NoRoute(func(ctx *gin.Context) {
        ctx.HTML(404, "404.html", nil)
    })
 
    // 路由组暂略
 
    // 服务器端口,用服务器端口来访问地址
    ginServer.Run(":8081") // 不写的话默认是8080,也可以更改
}

API用法示例https://gin-gonic.com/zh-cn/docs/examples/

1. 基准测试

Q:基准测试是什么?

A:基准测试,也称为性能测试或压力测试,是一种用于测量系统或组件性能的测试。基准测试的目的是了解系统或组件在特定条件下的性能,并将结果与其他类似系统或组件进行比较。基准测试可用于评估各种类型的系统和组件,包括硬件、软件、网络和整个系统。

Q:什么时候需要基准测试呀?

A:基准测试通常涉及在被测系统或组件上运行特定工作负载或任务,并测量吞吐量、延迟时间、CPU使用率、内存使用率等各种性能指标。基准测试的结果可用于识别瓶颈和性能问题,并做出有关如何优化系统或组件以提高性能的明智决策。

有许多不同类型的基准测试,每种类型都有自己的指标和工作负载。常见的基准测试类型包括:

  • 人工基准测试:使用人工工作负载来测量系统或组件的性能。
  • 真实世界基准测试:使用真实世界的工作负载或场景来测量系统或组件的性能。
  • 压力测试:旨在将系统或组件推到极限,以确定在正常使用条件下可能不明显的性能问题

重要的是要知道基准测试不是一次性的活动,而是应该定期进行的活动,以评估系统的性能并检测随时间的消耗。

Q:什么样的基准测试结果是我们想要的呀?

A:

  • 在一定的时间内实现的总调用数,越高越好
  • 单次操作耗时(ns/op),越低越好
  • 堆内存分配 (B/op), 越低越好
  • 每次操作的平均内存分配次数(allocs/op),越低越好

2. Gin的特性与Jsoniter:

Gin v1 稳定的特性:

  • 零分配路由。
  • 仍然是最快的 http 路由器和框架。
  • 完整的单元测试支持。
  • 实战考验。
  • API 冻结,新版本的发布不会破坏你的代码。
  • Gin 项目可以轻松部署在任何云提供商上。

Gin 使用 encoding/json 作为默认的 json 包,但是你可以在编译中使用标签将其修改为 jsoniter。

?
1
$ go build -tags=jsoniter .

Jsoniter是什么?

  • json-iterator是一款快且灵活的JSON解析器,同时提供JavaGo两个版本。
  • json-iterator是最快的JSON解析器。它最多能比普通的解析器快10倍之多
  • 独特的iterator api能够直接遍历JSON ,极致性能、零内存分配
  • 从dsljson和jsonparser借鉴了大量代码。

下载依赖:go get github.com/json-iterator/go

二、GORM

0. 特性与安装:

  • 全功能 ORM
  • 关联 (Has One,Has Many,Belongs To,Many To Many,多态,单表继承)
  • Create,Save,Update,Delete,Find 中钩子方法
  • 支持 PreloadJoins 的预加载
  • 事务,嵌套事务,Save Point,Rollback To Saved Point
  • Context、预编译模式、DryRun 模式
  • 批量插入,FindInBatches,Find/Create with Map,使用 SQL 表达式、Context Valuer 进行 CRUD
  • SQL 构建器,Upsert,数据库锁,Optimizer/Index/Comment Hint,命名参数,子查询
  • 复合主键,索引,约束
  • Auto Migration
  • 自定义 Logger
  • 灵活的可扩展插件 API:Database Resolver(多数据库,读写分离)、Prometheus…
  • 每个特性都经过了测试的重重考验
  • 开发者友好
?
1
2
go get -u gorm.io/gorm
go get -u gorm.io/driver/sqlite

其他的补充内容:

  • Gorm是软删除,为了保证数据库的完整性

三、Navicat

新建连接 -> MySQL -> 连接名随便 -> 密码随便 -> 双击左侧打开 -> 右键information_schema -> 新建数据库 -> 名称crud-list -> 字符集utf8mb4

这里如果打开的时候报错navicat 1045 - access denied for user 'root'@'localhost' (using password: 'YES'),则需要查看自己的数据库本身的问题

四、Gin+Gorm的CRUD

连接数据库

编写测试代码,成功运行即可,但是这个时候还不能查看数据库是否被创建,

如果要看我们需要定义结构体,然后定义表迁移,具体代码如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
package main
 
import (
    "fmt"
    "time"
 
    // "gorm.io/driver/sqlite"
    "github.com/gin-gonic/gin"
    "gorm.io/driver/mysql"
    "gorm.io/gorm"
)
 
func main() {
    // 如何连接数据库 ? MySQL + Navicat
    // 需要更改的内容:用户名,密码,数据库名称
    dsn := "root:BqV?eGcc_1o+@tcp(127.0.0.1:3306)/crud-list?charset=utf8mb4&parseTime=True&loc=Local"
    db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
 
    fmt.Println("db = ", db)
    fmt.Println("err = ", err)
 
    // 连接池
    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
    }
 
    // 迁移
    db.AutoMigrate(&List{})
 
    // 接口
    r := gin.Default()
 
    // 端口号
    PORT := "3001"
    r.Run(":" + PORT)
}

定义好之后我们运行,没有报错并且在终端显示出来3001就是正确的,这个时候我们可以去Navicat里面查看crud-list下面的"表",刷新后发现有一个lists产生,那就是对的了。

但是这个时候我们存在两个问题:

  • 没有主键:在struct里添加gorm.Model来解决,Ctrl+左键可以查看model
  • 表里面的名称变成了复数:详见文档的高级主题-GORM配置里,在*db*, *err* *:=* gorm.Open(mysql.Open(dsn), *&*gorm.Config{})里面添加一段话即可

更改完成之后我们要先在Navicat里面把原来的表lists删掉才能重新创建,这个时候我们重新运行,就会发现表单里面多了很多东西

结构体定义与优化

例如:

?
1
`gorm:"type:varchar(20); not null" json:"name" binding:"required"`

需要注意的是:

  • 结构体里面的变量(Name)必须首字母大写,否则创建不出列,会被自动忽略
  • gorm指定类型
  • json表示json接收的时候的名称
  • binding required表示必须传入

CRUD接口

?
1
2
3
4
5
6
// 测试
r.GET("/", func(c *gin.Context) {
    c.JSON(200, gin.H{
        "message": "请求成功",
    })
})

编写完这一段之后运行代码,然后去postman里面新建一个GET接口127.0.0.1:3001然后send一下,出现请求成功就请求成功了。

增也是一样,写好之后直接用如下JSON来测试就可以:

?
1
2
3
4
5
6
7
{
    "name" : "张三",
    "state" : "在职",
    "phone" : "13900000000",
    "email" : "6666@qq.com",
    "address" : "二仙桥成华大道"
}

返回:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
{
    "code": "200",
    "data": {
        "ID": 1,
        "CreatedAt": "2023-01-24T09:27:36.73+08:00",
        "UpdatedAt": "2023-01-24T09:27:36.73+08:00",
        "DeletedAt": null,
        "name": "张三",
        "state": "在职",
        "phone": "13900000000",
        "email": "6666@qq.com",
        "address": "二仙桥成华大道"
    },
    "msg": "添加成功"
}

这时候也可以在数据库里看到这条数据

删除也是一样,编写完运行之后添加一个DELETE接口,然后输入127.0.0.1:3001/user/delete/2之后send就可以看到返回了(前提是有数据)

?
1
2
3
4
{
    "code": 200,
    "msg": "删除成功"
}

如果是删除了不存在的id,就会返回

?
1
2
3
4
{
    "code": 400,
    "msg": "id没有找到,删除失败"
}

顺带一提,事实上这个代码还可以优化,这里的if else太多了,后面优化的时候有错误直接return

修改也是一样,示例:

?
1
2
3
4
5
6
7
{
    "name" : "张三",
    "state" : "离职",
    "phone" : "13900000000",
    "email" : "6666@qq.com",
    "address" : "二仙桥成华大道"
}

返回:

?
1
2
3
4
{
    "code": 200,
    "msg": "修改成功"
}

查询分为两种:

  • 条件查询
  • 分页查询

条件查询的话,直接写好了请求127.0.0.1:3001/user/list/王五

返回:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
{
    "code": "200",
    "data": [
        {
            "ID": 3,
            "CreatedAt": "2023-01-24T10:06:25.305+08:00",
            "UpdatedAt": "2023-01-24T10:06:25.305+08:00",
            "DeletedAt": null,
            "name": "王五",
            "state": "在职",
            "phone": "13100000000",
            "email": "8888@qq.com",
            "address": "八仙桥成华大道"
        }
    ],
    "msg": "查询成功"
}

全部 / 分页查询的话

譬如说请求是:127.0.0.1:3001/user/list?pageNum=1&pageSize=2

意思就是查询第一页的两个

返回:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
{
    "code": 200,
    "data": {
        "list": [
            {
                "ID": 3,
                "CreatedAt": "2023-01-24T10:06:25.305+08:00",
                "UpdatedAt": "2023-01-24T10:06:25.305+08:00",
                "DeletedAt": null,
                "name": "王五",
                "state": "在职",
                "phone": "13100000000",
                "email": "8888@qq.com",
                "address": "八仙桥成华大道"
            }
        ],
        "pageNum": 1,
        "pageSize": 2,
        "total": 2
    },
    "msg": "查询成功"
}

如果请求是:127.0.0.1:3001/user/list

返回:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
{
    "code": 200,
    "data": {
        "list": [
            {
                "ID": 1,
                "CreatedAt": "2023-01-24T09:27:36.73+08:00",
                "UpdatedAt": "2023-01-24T09:55:20.351+08:00",
                "DeletedAt": null,
                "name": "张三",
                "state": "离职",
                "phone": "13900000000",
                "email": "6666@qq.com",
                "address": "二仙桥成华大道"
            },
            {
                "ID": 3,
                "CreatedAt": "2023-01-24T10:06:25.305+08:00",
                "UpdatedAt": "2023-01-24T10:06:25.305+08:00",
                "DeletedAt": null,
                "name": "王五",
                "state": "在职",
                "phone": "13100000000",
                "email": "8888@qq.com",
                "address": "八仙桥成华大道"
            }
        ],
        "pageNum": 0,
        "pageSize": 0,
        "total": 2
    },
    "msg": "查询成功"
}

完整代码如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
package main
 
import (
    "fmt"
    "strconv"
    "time"
 
    // "gorm.io/driver/sqlite"
    "github.com/gin-gonic/gin"
    "gorm.io/driver/mysql"
    "gorm.io/gorm"
    "gorm.io/gorm/schema"
)
 
func main() {
    // 如何连接数据库 ? MySQL + Navicat
    // 需要更改的内容:用户名,密码,数据库名称
    dsn := "root:password@tcp(127.0.0.1:3306)/database?charset=utf8mb4&parseTime=True&loc=Local"
    db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{
        NamingStrategy: schema.NamingStrategy{
            SingularTable: true,
        },
    })
 
    fmt.Println("db = ", db)
    fmt.Println("err = ", err)
 
    // 连接池
    sqlDB, err := db.DB()
    // SetMaxIdleConns 设置空闲连接池中连接的最大数量
    sqlDB.SetMaxIdleConns(10)
    // SetMaxOpenConns 设置打开数据库连接的最大数量。
    sqlDB.SetMaxOpenConns(100)
    // SetConnMaxLifetime 设置了连接可复用的最大时间。
    sqlDB.SetConnMaxLifetime(10 * time.Second) // 10秒钟
 
    // 结构体
    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"`
    }
 
    // 迁移
    db.AutoMigrate(&List{})
 
    // 接口
    r := gin.Default()
 
    // 测试
    // r.GET("/", func(c *gin.Context) {
    //  c.JSON(200, gin.H{
    //      "message": "请求成功",
    //  })
    // })
 
    // 业务码约定:正确200,错误400
 
    // 增
    r.POST("/user/add", func(ctx *gin.Context) {
        // 定义一个变量指向结构体
        var data List
        // 绑定方法
        err := ctx.ShouldBindJSON(&data)
        // 判断绑定是否有错误
        if err != nil {
            ctx.JSON(200, gin.H{
                "msg""添加失败",
                "data": gin.H{},
                "code": "400",
            })
        } else {
            // 数据库的操作
            db.Create(&data) // 创建一条数据
            ctx.JSON(200, gin.H{
                "msg""添加成功",
                "data": data,
                "code": "200",
            })
        }
    })
 
    // 删
    // 1. 找到对应的id对应的条目
    // 2. 判断id是否存在
    // 3. 从数据库中删除 or 返回id没有找到
 
    // Restful编码规范
    r.DELETE("/user/delete/:id", func(ctx *gin.Context) {
        var data []List
        // 接收id
        id := ctx.Param("id") // 如果有键值对形式的话用Query()
        // 判断id是否存在
        db.Where("id = ? ", id).Find(&data)
        if len(data) == 0 {
            ctx.JSON(200, gin.H{
                "msg""id没有找到,删除失败",
                "code": 400,
            })
        } else {
            // 操作数据库删除(删除id所对应的那一条)
            // db.Where("id = ? ", id).Delete(&data) <- 其实不需要这样写,因为查到的data里面就是要删除的数据
            db.Delete(&data)
 
            ctx.JSON(200, gin.H{
                "msg""删除成功",
                "code": 200,
            })
        }
 
    })
 
    // 改
    r.PUT("/user/update/:id", func(ctx *gin.Context) {
        // 1. 找到对应的id所对应的条目
        // 2. 判断id是否存在
        // 3. 修改对应条目 or 返回id没有找到
        var data List
        id := ctx.Param("id")
        // db.Where("id = ?", id).Find(&data) 可以这样写,也可以写成下面那样
        // 还可以再Where后面加上Count函数,可以查出来这个条件对应的条数
        db.Select("id").Where("id = ? ", id).Find(&data)
        if data.ID == 0 {
            ctx.JSON(200, gin.H{
                "msg""用户id没有找到",
                "code": 400,
            })
        } else {
            // 绑定一下
            err := ctx.ShouldBindJSON(&data)
            if err != nil {
                ctx.JSON(200, gin.H{
                    "msg""修改失败",
                    "code": 400,
                })
            } else {
                // db修改数据库内容
                db.Where("id = ?", id).Updates(&data)
                ctx.JSON(200, gin.H{
                    "msg""修改成功",
                    "code": 200,
                })
            }
        }
    })
 
    // 查
    // 第一种:条件查询,
    r.GET("/user/list/:name", func(ctx *gin.Context) {
        // 获取路径参数
        name := ctx.Param("name")
        var dataList []List
        // 查询数据库
        db.Where("name = ? ", name).Find(&dataList)
        // 判断是否查询到数据
        if len(dataList) == 0 {
            ctx.JSON(200, gin.H{
                "msg""没有查询到数据",
                "code": "400",
                "data": gin.H{},
            })
        } else {
            ctx.JSON(200, gin.H{
                "msg""查询成功",
                "code": "200",
                "data": dataList,
            })
        }
    })
 
    // 第二种:全部查询 / 分页查询
    r.GET("/user/list", func(ctx *gin.Context) {
        var dataList []List
        // 查询全部数据 or 查询分页数据
        pageSize, _ := strconv.Atoi(ctx.Query("pageSize"))
        pageNum, _ := strconv.Atoi(ctx.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 {
            ctx.JSON(200, gin.H{
                "msg""没有查询到数据",
                "code": 400,
                "data": gin.H{},
            })
        } else {
            ctx.JSON(200, gin.H{
                "msg""查询成功",
                "code": 200,
                "data": gin.H{
                    "list":     dataList,
                    "total":    total,
                    "pageNum":  pageNum,
                    "pageSize": pageSize,
                },
            })
        }
 
    })
 
    // 端口号
    PORT := "3001"
    r.Run(":" + PORT)
}

到此这篇关于Gin+Gorm实战CRUD的文章就介绍到这了,更多相关Gin Gorm CRUD内容请搜索服务器之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持服务器之家!

原文链接:https://juejin.cn/post/7192053234620432441

延伸 · 阅读

精彩推荐
  • Golanggolang 64位linux环境下编译出32位程序操作

    golang 64位linux环境下编译出32位程序操作

    这篇文章主要介绍了golang 64位linux环境下编译出32位程序操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...

    天天water12162021-03-15
  • GolangGo递归修改文件名的实例代码

    Go递归修改文件名的实例代码

    这篇文章主要介绍了Go递归修改文件名的实例代码,本文给大家介绍的非常详细,对大家的学习或工作具有一定的饿参考借鉴价值,需要的朋友可以参考下...

    包世龙4012021-03-24
  • Golang小学生也能看懂的Golang异常处理recover panic

    小学生也能看懂的Golang异常处理recover panic

    在其他语言里,宕机往往以异常的形式存在,底层抛出异常,上层逻辑通过 try/catch 机制捕获异常,没有被捕获的严重异常会导致宕机,go语言追求简洁,优...

    毛毛是一只狗7792021-11-16
  • GolangGo工程化如何在整洁架构中使用事务?

    Go工程化如何在整洁架构中使用事务?

    事务的能力是在 repo 上提供的,所以我们需要在 repo 层提供一个事务接口,然后在 usecase 中进行调用,保证是事务执行的就行。...

    mohuishou11372021-12-28
  • Golanggolang实现简单rpc调用过程解析

    golang实现简单rpc调用过程解析

    这篇文章主要介绍了golang实现简单rpc调用,包括RPC具体实现结合实例代码给大家讲解的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友...

    神技圈子4522022-10-07
  • Golang前后端分离必备, Golang Gin中如何使用JWT(JsonWebToken)中间件?

    前后端分离必备, Golang Gin中如何使用JWT(JsonWebToken)中间件?

    JSON Web Token(缩写 JWT)是目前最流行的跨域认证解决方案,也是目前前后端分离项目中普遍使用的认证技术. 本文介绍如何在Golang Gin Web框架中使用JWT认证中...

    云原生云14962020-12-15
  • GolangGolang实现HTTP编程请求和响应

    Golang实现HTTP编程请求和响应

    本文主要介绍了Golang实现HTTP编程请求和响应,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着...

    itzhuzhu.8792022-08-08
  • Golanggolang interface判断为空nil的实现代码

    golang interface判断为空nil的实现代码

    这篇文章主要介绍了golang interface判断为空nil的实现代码,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...

    翔云1234565492021-05-27