gin+MySQL简单实现数据库查询
利用 gin 项目搭建一个简易的后端系统。
一个简易的 HTTP 响应接口
首先在 go 工作区的终端输入这条指令:
go get -u github.com/gin-gonic/gin
将 gin 项目的相关依赖保存到本地。
在终端生成 go mod 包管理文件:
go mod init
再创建一个 main.go 文件:
package main
import "github.com/gonic-gin/gin"
func main() {
r := gin.Default()
r.GET("/test", func(c *gin.Context) {
c.JSON(200, gin.H{
"message": "test",
})
})
r.Run(":9999") // 运行在9999端口
}
因为我是在 wsl 运行这个项目,所以还需要先获取虚拟机的 ip,然后用 curl 测试:
curl http://<ip>:9999/ping
返回结果:
{"message":"test"}
连接 MySQL
关于 MySQL 操作这部分,一开始想着简单,只学了查询数据库部分,大多数踩得坑都是在查询部分,后面觉得要举一反三,就在原来基础上又写了添加的部分,在添加数据这一块写的不是很详细。
添加
先把基本的框架写出来,这里我连接的数据库结构体如下:
type SqlUser struct {
User_id string `json:"user_id" from:"user_id"`
User_name string `json:"user_name" from:"user_name"`
}
添加单条数据:
Db, _ := sql.Open("mysql", "<username>:<password>@(localhost:3306)/<yourDatabase>")
router.POST("/add", func(c *gin.Context) {
User_id := c.Request.FormValue("User_id")
User_name := c.Request.FormValue("User_name")
result, err := db.SqlDB.Exec("INSERT INTO t_user (user_id,user_name) VALUE(?,?)", u.User_id, u.User_name)
if err != nil {
log.Fatalln(err)
}
id, err := result.LastInsertId()
if err != nil {
log.Fatalln(err)
}
msg := fmt.Sprintf("insert successfully %d", id)
c.JSON(http.StatusOK, gin.H{
"msg": msg,
"user_name": User_name,
})
})
r.Run(":9999")
执行非 query 操作,使用 Exec 方法,使用 curl 进行测试:
curl -X POST http://<ip>:9999/add -d "User_id=2&User_name=aaa"
返回结果如下:
{"msg":"insert successfully 1","user_name":"test"}
查询
单条查询如下
r.GET("/test/:id", func(c *gin.Context) {
id: c.Param("id")
var u SqlUser
err := Db.QueryRow("select user_id,user_name from t_user where user_id =?", id).Scan(&u.user_id, &u.user_name)
if err != nil {
log.Fatalln(err)
c.JSON(http.StatusOK, gin.H{
"user": nil,
})
return
}
c.JSON(http.StatusOK, gin.H{
"user": u.user_name,
})
})
然后用 curl 命令去测试:
curl http://<ip>:9999/test/1
会得到之前在 MySQL 中预先保存的条目,样例结构如下:
{"user":"test"}
然而,最开始我不是这样写的,当时的我,没整理好这个框架,直接在单文件里封装函数,下面是我的错误示范:
var Db *sql.DB
func QueryData(c *gin.Context) {
id := c.Param("id")
var u SqlUser
err := Db.QueryRow("select user_id,user_name from t_user where user_id =?", id).Scan(&u.user_id, &u.user_name)
if err != nil {
log.Fatalln(err)
c.JSON(200, gin.H{
"user": nil,
})
return
}
c.JSON(200, gin.H{
"user": u.user_name,
})
}
func main() {
Db, _ = sql.Open("/*mysql link*/")
// 略去了这个问题相关的代码
r.GET("/test/:id", QueryData)
r.Run(":9999")
}
然后就出现这样的错误:
runtime error: invalid memory address or nil pointer dereference
必应上的结果是:指针声明后未初始化就赋值。
这个 bug 困扰了我一整天,然后就开始了痛苦的 debug,main 函数的 router 应该是没问题的,接口测试正常,那问题应该是出在 QueryData 上,那只能一个个打印变量测试吧,测试到 Db 时,发现这个变量为 nil,然后紧接着抛出刚才提到的错误。好吧,这个错误我在之前也偶尔犯过,虽然在 main 中对其赋了值,但作用域不一样,所以还是无法传值,那只好去考虑下框架了。
修改
修改单条数据:
router.PUT("/person/:id", func(c *gin.Context) {
cid := c.Param("id")
id, err := strconv.Atoi(cid)
person := Person{Id: id}
err = c.Bind(&person)
if err != nil {
log.Fatalln(err)
}
stmt, err := Db.Prepare("UPDATE person SET user_name=? where user_id=?")
if err != nil {
log.Fatalln(err)
}
defer stmt.Close()
rs, err := stmt.Exec(person.user_name, person.user_id)
if err != nil {
log.Fatalln(err)
}
ra, err := rs.RowsAffected()
if err != nil {
log.Fatalln(err)
}
msg := fmt.Sprintf("UPDATE person %d successful %d", person.user_id, ra)
c.JSON(http.StatusOK, gin.H{
"msg": msg,
})
})
urlencode 方式更新:
curl -X PUT http://<ip>:9999/person/1 -d "user_id=1&user_name=temp"
json 方式更新:
curl -X PUT http://<ip>:9999/person/1 -H "Content-Type: application/json" -d '{"user_name":"temp"}'
删除
router.DELETE("person/:id", func(c *gin.Context) {
cid := c.Param("id")
id, err := strconv.Atoi(cid)
if err != nil {
log.Fatalln(err)
}
rs, err := db.Exec("DELETE FROM person from where user_id=?", id)
if err != nil {
log.Fatalln(err)
}
ra, err := rs.RowsAffected()
if err != nil {
log.Fatalln(err)
}
msg := fmt.Sprintf("DELETE person %d successful %d", id, ra)
c.JSON(http.StatusOK, gin.H{
"msg": msg,
})
})
直接使用删除接口
curl -X "DELETE" http://<ip>:9999/person/1
这里的 DELETE 参数必须在双引号内,不然不会被识别为 delete 请求。
搭建框架
参考了大佬的做法 [1],我也来创建我的目录:
ginExample tree
.
├── api
│ └── query.go
├── database
│ └── mysql.go
├── main.go
├── models
│ └── queryUser.go
├── router.go
├── go.mod
└── go.sum
这里的话以添加、查询功能为例,增删改同理,具体可以查看文末的参考链接。
api 存放 handler 函数,model 存放数据模型。
数据库处理
// mysql.go
package database
import (
"database/sql"
_ "github.com/go-sql-driver/mysql"
"log"
)
var SqlDB *sql.DB
func init() {
var err error
SqlDB, err = sql.Open("mysql", "root:jaydenmysql@(127.0.0.1:3306)/microtest")
if err != nil {
log.Fatalln(err.Error())
}
err = SqlDB.Ping()
if err != nil {
log.Fatalln(err.Error())
}
}
因为在别的包会用到 SqlDB 这个变量,因此必须大写 (golang 只有大写开头变量才是 public 类变量)
数据 model 封装
抽离出 SqlUser 结构体以及对应的方法:
package models
import (
db "ginExample/database"
"log"
)
type SqlUser struct {
User_id string `json:"user_id" from:"user_id"`
User_name string `json:"user_name" from:"user_name"`
}
func (u *SqlUser) GetUser(id string) SqlUser {
err := db.SqlDB.QueryRow("select user_id,user_name from t_user where user_id =?", id).Scan(&u.User_id, &u.User_name)
if err != nil {
log.Println(err)
u.User_name = "nil"
return *u
}
return *u
}
func (u *SqlUser) AddUser() (id int64, err error) {
result, err := db.SqlDB.Exec("INSERT INTO t_user (user_id,user_name) VALUE(?,?)", u.User_id, u.User_name)
if err != nil {
log.Fatalln(err)
return
}
id, err = result.LastInsertId()
if err != nil {
log.Fatalln(err)
return
}
fmt.Println(id)
return id, err
}
func (u *SqlUser) UpdateUser() int64 {
stmt, err := db.SqlDB.Prepare("UPDATE t_user SET user_name=? where user_id=?")
if err != nil {
log.Fatalln(err)
}
rs, err := stmt.Exec(u.User_name, u.User_id)
if err != nil {
log.Fatalln(err)
}
ra, err := rs.RowsAffected()
if err != nil {
log.Fatalln(err)
}
return ra
}
func (u *SqlUser) DeleteUser(id string) int64 {
rs, err := db.SqlDB.Exec("DELETE FROM t_user where user_id=?", id)
if err != nil {
log.Fatalln(err)
}
ra, err := rs.RowsAffected()
if err != nil {
log.Fatalln(err)
}
return ra
}
handler
然后把具体的 handler 封装到 api 中,handler 操作数据库,因此会引用 model 包。
package api
import (
"fmt"
"net/http"
. "ginExample/models"
"github.com/gin-gonic/gin"
)
func IndexApi(c *gin.Context) {
c.String(http.StatusOK, "It works")
}
func GetUserApi(c *gin.Context) {
var u SqlUser
id := c.Param("id")
u = u.GetUser(id)
c.JSON(200, gin.H{
"user": u.User_name,
})
}
func AddUserApi(c *gin.Context) {
User_id := c.Request.FormValue("User_id")
User_name := c.Request.FormValue("User_name")
// 这里的大小写一定要对应上结构体内的变量名
u := models.SqlUser{User_id: User_id, User_name: User_name}
rows, err := u.AddUser()
if err != nil {
log.Fatalln(err)
}
msg := fmt.Sprintf("insert successfully %d\n", rows)
c.JSON(200, gin.H{
"msg": msg,
"user_name": User_name,
})
}
func UpdateUserApi(c *gin.Context) {
User_id := c.Request.FormValue("User_id")
User_name := c.Request.FormValue("User_name")
u := models.SqlUser{User_id: User_id, User_name: User_name}
row := u.UpdateUser()
msg := fmt.Sprintf("update successful %d", row)
c.JSON(200, gin.H{
"msg": msg,
})
}
func DeleteUserApi(c *gin.Context) {
var u models.SqlUser
id := c.Param("id")
row := u.DeleteUser(id)
msg := fmt.Sprintf("DELETE user successful %d", row)
c.JSON(200, gin.H{
"msg": msg,
})
}
router
最后就是把路由抽离出来:
//router.go
package main
import (
"github.com/gin-gonic/gin"
. "ginExample/api"
)
func initRouter() *gin.Engine {
router := gin.Default()
router.GET("/", IndexApi)
router.GET("/query/:id", GetUserApi)
router.POST("/add", api.AddUserApi)
router.PUT("/update/:id", api.UpdateUserApi)
router.DELETE("/delete/:id", api.DeleteUserApi)
return router
}
app 入口
最后就是 main 的 app 入口,将路由导入,同时在 main 即将结束时,关闭全局数据库连接池。
package main
import db "ginExample/database"
func main() {
defer db.SqlDB.Close()
router := initRouter()
router.Run(":9999")
}
这里运行项目的话,不能像之前简单地使用 go run main.go ,因为 main 包含 main.go 和 router.go,因此要运行 go run *.go,如果最终编译二进制项目,则运行 go build -o app。
测试结果和上面是一样的,至此,基本的访问、操作数据库功能实现。
参考链接
gin+MySQL简单实现数据库查询的更多相关文章
- Gin实战:Gin+Mysql简单的Restful风格的API(二)
上一篇介绍了Gin+Mysql简单的Restful风格的API,但代码放在一个文件中,还不属于restful风格,接下来将进行进一步的封装. 目录结构 ☁ gin_restful2 tree . ├─ ...
- PHP简单获取数据库查询结果并返回JSON
<?php header("Content-type:text/html;charset=utf-8"); //连接数据库 $con = mysql_connect(&quo ...
- Gin实战:Gin+Mysql简单的Restful风格的API
我们已经了解了Golang的Gin框架.对于Webservice服务,restful风格几乎一统天下.Gin也天然的支持restful.下面就使用gin写一个简单的服务,麻雀虽小,五脏俱全.我们先以一 ...
- WPF简单的数据库查询
做一个简单WPF连接数据库的 控件类型和名称:DataGrid:dataGrid Button1 :Button1 Button: Button2 ...
- 使用IDEA创建Spring boot项目,继承mybaits。并进行简单的数据库查询操作
本文讲的是使用IEDA创建Spring boot项目,对于环境安装需要自行准备,如JDK1.8.Maven 3.3.IDEA编译器.Mysql5.7等需事前准备好. 1.创建Spring boot项目 ...
- MySQL千万级数据库查询怎么提高查询效率
在实际项目中,当MySQL表的数据达到百万级别时候,普通查询效率直线下降,而且当使用的where条件较多,其查询效率是让人无法容忍的.假如一个taobao订单查询详情要几十秒,可想而知的用户体验是多差 ...
- java简单的数据库查询(SQLServer数据库)
1.数据库链接类 import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; pu ...
- c#简单的数据库查询与绑定DataGridView。
1配置文件 (两种写法) <connectionStrings> <add name="connStr" connectionString="se ...
- 数据库 之MySQL 简单教程
So Easy系列之MySQL数据库教程 1. 数据库概述 1.1. 数据库概述 数据库(Database)是按照数据结构来组织.存储和管理数据的仓库,它产生于距今六十多年前,随着信息技术和 ...
- C#与mysql做ASP.NET网页数据库查询速度测试
两种方法是:1,使用mysql数据库的存储过程:2,C#编码,做网页后台与mysql数据库连接,前台测试显示测试过结果下面我将分别讲解两种方法的具体实现. 1,使用mysql数据库的存储过程插入万条大 ...
随机推荐
- 单机运行环境搭建之 --CentOS-6.4安装MySQL 5.6.10并修改MySQL的root用户密码
单机运行环境搭建之 --CentOS-6.4安装MySQL 5.6.10并修改MySQL的root用户密码 Mysql 5.5以后使用了CMake进行安装,参考与以前的区别请参考: http://ww ...
- Spring Boot+mybatis集成数据库访问
1.整合druid数据源 1.1 单数据源配置 <dependency> <groupId>com.alibaba</groupId> <artifactId ...
- Spring JDBCTemplet通用RowMapper帮助类
1 import java.lang.reflect.Method; 2 import java.math.BigDecimal; 3 import java.math.BigInteger; 4 i ...
- 力扣183(MySQL)-从不订购的客户(简单)
题目: 某网站包含两个表,Customers 表和 Orders 表.编写一个 SQL 查询,找出所有从不订购任何东西的客户. Customers 表: Orders 表: 解题思路: 需要查询出没 ...
- HarmonyOS NEXT应用开发案例——二级联动
介绍 本示例主要介绍了List组件实现二级联动(Cascading List)的场景. 该场景多用于短视频中拍摄风格的选择.照片编辑时的场景的选择. 效果图预览 使用说明: 滑动二级列表侧控件,一级列 ...
- 一种关于低代码平台(LCDP)建设实践与设计思路
简介: 作者在负责菜鸟商业中心CRM系统开发过程中发现有一个痛点:业务线很多,每个业务线对同一个页面都有个性化布局和不同的字段需求,而他所在的团队就3个人,那么在资源有限的情况下该如何支撑呢?本文就降 ...
- 比心云平台基于阿里云容器服务 ACK 的弹性架构实践
简介:本文主要探讨比心云平台如何利用阿里云容器服务 ACK,来构建应用弹性架构,进一步优化计算成本. 作者:韩韬|比心技术 前言 应用容器化改造后,不可避免地会面临这样一个问题:Kubernetes ...
- 阿里的 RocketMQ 如何让双十一峰值之下 0 故障?
简介: 2020 年双十一交易峰值达到 58.3 W 笔/秒,消息中间件 RocketMQ 继续数年 0 故障丝般顺滑地完美支持了整个集团大促的各类业务平稳. 作者 | 愈安来源 | 阿里巴巴云原生公 ...
- IIncrementalGenerator 获取引用程序集的所有类型
本文告诉大家如何在使用 IIncrementalGenerator 进行增量的 Source Generator 生成代码时,如何获取到当前正在分析的程序集所引用的所有的程序集,以及引用的程序集里面的 ...
- dotnet 修复 Uno 中文乱码
这是一个历史问题,在使用 Uno 展示中文的时候,如果设置 Uno 的底层使用 Skia 系进行渲染,那么将会因为中文字体问题,导致渲染出现乱码.此问题已被我修复,最佳解法是更新到最新版本 在上一篇博 ...