东东吖带你打通全栈,使用Go+MySQL实现最基础的CRUD

2,807 阅读5分钟

项目介绍

成为一名全栈工程师是许多程序员都渴望实现的梦想。但是这并不是一件容易的事情:它需要各种技能和知识,以及持续学习和实践的态度。

我最近做了一个项目叫做《go-vue-react》,这是一个前后端分离的Web应用程序。在这个项目中,我使用了三种不同的技术栈:Go语言、Vue 3 和React 18。其中后端是使用的Go语言,数据库采用的是Mysql。前端做了两套,分别使用了Vue 3 和 React 18 技术栈, 大家根据自己的需要,自行选择学习其中的一个或者多个项目。下面是我的经验分享:

go-vue-react项目

全栈启蒙版本分支:分支go-vue-react-base

my github 主页:github.com/github-kiko

前言

本文将介绍如何使用Go语言和MySQL数据库实现最基础的CRUD操作。如果你是一个零基础的学习者,那么这篇文章一定会对你有所帮助。在开始之前,请确保你已经安装了Go和MySQL。

go环境配置

首先,让我们写一个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

数据库可视化工具 Navicat

Navicat是一款功能强大的数据库管理工具,支持多种数据库类型,如MySQL、PostgreSQL、Oracle等。它提供了一个直观的用户界面,可以帮助用户轻松地管理和操作各种数据库。

总的来说,Navicat是一个非常强大的工具,可以大大提高数据库管理和操作的效率和便利性。无论您是一名数据库管理员还是开发人员,都值得一试。

在做项目的时候,我们需要在Navicat创建一个名为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)

代码中的root12345678分别是你的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,

			})
		}
	})

总结

  1. 主键缺失问题:数据库都是逻辑删除,并不会真正的删除数据,会有ID,创建时间、更新数据,删除时间等,这叫做主键,没有这些是不符合规范的。可以在结构体中添加gorm.Model 解决
  2. 表名复数问题:在创建表的时候,数据库的表会带s,但实际我们创建的数据库表是不带s的,可以通过gorm的配置解决
NamingStrategy: schema.NamingStrategy{
		SingularTable: true,
		},
  1. 数据库操作时首字母大写,而返回给前端的json是小写,所以必须在结构体中定义为首字母大写,如果你在结构体中不指定json为小写,因为数据库是大写,那么返回给前端的数据也会是大写,所以需要在结构体中指定json为小写。

  2. 需要进技术交流群的,请加我微信:fangdongdong_25,备注:go-vue-react