东东吖带你打通全栈,使用Go+MySQL实现最基础的CRUD
项目介绍
成为一名全栈工程师是许多程序员都渴望实现的梦想。但是这并不是一件容易的事情:它需要各种技能和知识,以及持续学习和实践的态度。
我最近做了一个项目叫做"go-vue-react",这是一个前后端分离的Web应用程序。在这个项目中,我使用了三种不同的技术栈:Go语言、Vue 3 和React 18。其中后端是使用的Go语言,数据库采用的是Mysql。前端做了两套,分别使用了Vue 3 和 React 18 技术栈, 大家根据自己的需要,自行选择学习其中的一个或者多个项目。下面是我的经验分享:
全栈启蒙项目:go-vue-react-base
github:github.com/github-kiko…
全栈学习成长史
2023年3月
- 2023-03-11, 项目发起,创建了go-vue-react项目
- 2023-03-12, 使用go+mysql实现了后端最基础的crud
- 2023-03-18, 使用vue3+elementPlus完成了crud,完成了第一个全栈小项目
未来学习计划
- 2023-03-xx, 将vue3中的js替换为ts,学习ts的使用
2023年4月
- 2023-04-xx, 使用react18+ts+Material-UI完成了react技术栈的crud
- 2023-04-xx, 新增路由、调整完善go、vue、react目录结构,规划企业级项目
- 2023-04-xx, 完成全栈学习成长史功能,记录自己的全栈学习成长史,激发自己更好的学习
前言
本文将介绍如何使用Go语言和MySQL数据库实现最基础的CRUD操作。如果你是一个零基础的学习者,那么这篇文章一定会对你有所帮助。在开始之前,请确保你已经安装了Go和MySQL。
Hello, World!
首先,让我们写一个Go版的Hello, World程序来确认我们的Go环境是否正确配置:
package main
import "fmt"
func main() {
fmt.Println("Hello, World!")
}
这个程序打印出了“Hello, World!”。如果你已经成功地编译并运行了这个程序,那么你可以进入下一步了。
MySQL的下载安装
接下来我们需要下载安装MySQL数据库。请遵循官方文档进行安装指导:dev.mysql.com/doc/mysql-i…。
安装完成后,你需要启动MySQL服务。在Windows环境下,你可以在命令行中输入以下命令:
net start mysql
数据库可视化工具 vavicat
Vavicat是一款功能强大的数据库管理工具,支持多种数据库类型,如MySQL、PostgreSQL、Oracle等。它提供了一个直观的用户界面,可以帮助用户轻松地管理和操作各种数据库。
总的来说,Vavicat是一个非常强大的工具,可以大大提高数据库管理和操作的效率和便利性。无论您是一名数据库管理员还是开发人员,都值得一试。
在做项目的时候,我们需要在vavicat创建一个名为crud-list的数据库,账号为root,密码为12345678,端口为3306。
连接MySQL数据库
在Go中连接MySQL数据库需要使用第三方库。我们选择使用"gorm.io/driver/mysql"
库。通过以下命令来安装该库:
go get -u github.com/gorm.io/driver/mysql
安装完成后,我们可以使用以下代码来连接MySQL数据库:
// 连接数据库
dsn := "root:12345678@tcp(127.0.0.1:3306)/crud-list?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)
代码中的root
和12345678
分别是你的MySQL用户名和密码,crud-list
是你要连接的数据库名称。如果连接成功,程序将输出db
。
连接池
// GORM 使用database/sql维护连接池
sqlDB, err := db.DB()
// SetMaxIdleConns 设置空闲连接池中连接的最大数量
sqlDB.SetMaxIdleConns(10)
// SetMaxOpenConns 设置打开数据库连接的最大数量。
sqlDB.SetMaxOpenConns(100)
// SetConnMaxLifetime 设置了连接可复用的最大时间。
sqlDB.SetConnMaxLifetime(10 * 1000) //10秒钟
结构体和自动迁移
// 结构体 数据库大写,json小写
type List struct {
gorm.Model //解决主键缺失
ID uint8 `json:"id"`
Name string `json:"name"`
Age uint8 `json:"age"`
School string `json:"school"`
Phone string `json:"phone"`
Address string `json:"address"`
}
//自动迁移
db.AutoMigrate(&List{})
// 接口
r:=gin.Default()
// 端口
PORT:="3000"
r.Run(":"+PORT)
结构体数据库是大写,返回给前端的json是小写, gorm.model是解决主键缺失问题,不然数据库不满足规范,没有ID和创建更新时间等。自动迁移会自动创建表字段,方便省事。
实现CRUD操作
插入数据
// 增加:
r.POST("/list/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":gin.H{},
"code":200,
})
}
})
删除数据
// 删除
r.DELETE("/list/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,
})
}
})
更新数据
// 修改
r.PUT("/list/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.Where("id=?",id).Updates(&data)
c.JSON(200,gin.H{
"msg":"修改成功",
"code":200,
})
}
}
})
查询数据
// 查询
r.GET("/list/query",func(c*gin.Context){
// 获取路径参数
var dataList []List
//获取分页参数
pageSize, _ := strconv.Atoi(c.Query("pageSize"))
page, _ := strconv.Atoi(c.Query("page"))
// 计算偏移量和限制数量,支持更灵活的分页参数设置
offset := (page - 1) * pageSize
limit := pageSize
// 返回一个总数
var total int64
name := c.Query("name")
fmt.Printf("查询条件:%s\n", name)
fmt.Printf("offset: %d\n", offset)
fmt.Printf("limit: %d\n", limit)
db.Model(&List{}).Where("name LIKE ?", fmt.Sprintf("%%%s%%", name)).Count(&total)
// 条件查询并进行分页
db.Where("name LIKE ?", fmt.Sprintf("%%%s%%", name)).Offset(offset).Limit(limit).Find(&dataList)
//判断是否查询到数据
if len(dataList)==0{
c.JSON(200,gin.H{
"msg":"没有查询到数据",
"code":200,
"data":gin.H{},
})
}else{
c.JSON(200,gin.H{
"msg":"查询成功",
"code":200,
"data":dataList,
"total":total,
})
}
})
总结
- 主键缺失问题:数据库都是逻辑删除,并不会真正的删除数据,会有ID,创建时间、更新数据,删除时间等,这叫做主键,没有这些是不符合规范的。可以在结构体中添加gorm.Model 解决
- 表名复数问题:在创建表的时候,数据库的表会带s,但实际我们创建的数据库表是不带s的,可以通过gorm的配置解决
NamingStrategy: schema.NamingStrategy{
SingularTable: true,
},
-
数据库操作时首字母大写,而返回给前端的json是小写,所以必须在结构体中定义为首字母大写,如果你在结构体中不指定json为小写,因为数据库是大写,那么返回给前端的数据也会是大写,所以需要在结构体中指定json为小写。
转载自:https://juejin.cn/post/7211476316252012603