使用gin搭建api后台系统之MySQL初步CURD
之前的文章介绍了gin框架的基础使用,现代的web系统,很少不与数据库打交道的,接下来的几篇文章,从日常使用的比较多的数据库MySQL, Mongodb, redis 来分别简单的介绍如何使用,这篇先介绍一下MySQL的使用。
大多数的文章教程都是以GORM为主,但是我对于ORM实在不感兴趣,本文会以原生的sql语言进行CURD操作。
数据库的初始化
我使用的是https://github.com/go-sql-driver/mysql
这个库,在项目目录中创建一个db的目录,再创建一个initdb.go,我将数据库的初始化操作放到这个文件中。
| package db
import (
"database/sql"
"fmt"
_ "github.com/go-sql-driver/mysql"
"time"
)
func InitDB() *sql.DB {
dsn := fmt.Sprintf("%s:%s@(%s)/%s?charset=%s&parseTime=true&loc=Local",
"root", "123456", "127.0.0.1:3306", "gintest", "utf8")
if conn, err := sql.Open("mysql", dsn); err != nil {
panic(err.Error())
} else {
conn.SetConnMaxLifetime(7 * time.Second) //设置空闲时间,这个是比mysql 主动断开的时候短
conn.SetMaxOpenConns(10)
conn.SetMaxIdleConns(10)
return conn
}
}
|
sql.Open
方法开始连接数据库,第一个参数为所要连接的数据库类型。第二个参数为dsn,可以理解为连接数据库的参数信息。
这里在连接以后,又对连接进行了三个设置。
conn.SetConnMaxLifetime
为设置连接的空闲时间,为什么要设置这个参数以及该设置多少是有讲究的。
有的MySQL服务器为了性能考虑,会设置主动断开空闲连接的,默认8个小时,但是一般的dba不会设置那么长,很有可能会设置10秒或者更短,所以这个参数要设置的更短,这个参数可以登录MySQL 服务器执行show global variables like '%timeout%';
来查看,有个wait_timeout
值,这里的值要设置比这个值更短。
conn.SetMaxOpenConns(10)
和conn.SetMaxOpenConns(10)
这两个方法会初始化一个连接池,保证池子里会有足够的连接,这个值要设置多少需要根据应用的并发情况。
但是SetMaxIdleConns
的值不要小于SetMaxOpenConns
设置的值,否则会导致频繁的创建连接。
官方的解释为
db.SetMaxIdleConns()
is recommended to be set same to db.SetMaxOpenConns()
. When it is smaller than SetMaxOpenConns()
, connections can be opened and closed much more frequently than you expect
这里还有个需要注意的是,在import 时,有个_ "github.com/go-sql-driver/mysql"
这行代码不能缺,该代码会执行一会init方法,初始化一些变量,如果没有这行的话,会由于一些变量未被初始化而panic .
一般情况下,我们会将数据库连接对象放到一个全局的变量中,然后将不同的数据库操作封装到dao中。
在项目目录下再创建一个global目录,再创建一个global.go文件, 在该文件中定义一些需要全局使用的变量.
| package global
import "database/sql"
var (
Mysql *sql.DB
)
|
之后在main.go中进行mysql变量的初始化
| package main
import (
"github.com/gin-gonic/gin"
"yyxtest/db"
"yyxtest/global"
"yyxtest/handlers"
)
func main() {
r := gin.Default()
global.Mysql = db.InitDB()
userv1_h := handlers.UserV1{}
userv1 := r.Group("/user/v1")
{
userv1.GET("/check", userv1_h.CheckUsers)
}
r.Run(":8080")
}
|
global.Mysql = db.InitDB()
这行代码为初始化全局的Mysql 对象, 之后我们创建一个dao文件夹,再创建一个userdao.go文件,这里封装数据库的查询. 先写一个查询所有记录的函数。
事先准备一个表,并插入几条数据

数据查询
在userdao.go中写入以下代码,
| package dao
import "yyxtest/global"
type user struct {
ID int `json:"id"`
Name string `json:"name"`
Age int `json:"age"`
}
func GetAllUsers() []user {
rows, err :=global.Mysql.Query("select * from user_infos")
if err != nil{
return nil
}
var persons = make([]user, 0)
for rows.Next() {
var a user
err := rows.Scan(&a.ID, &a.Name, &a.Age)
if err != nil {
return nil
}
persons = append(persons, a)
}
return persons
}
|
GetAllUsers
函数执行select * from user_infos
, 并且将该查询结果返回[]user 的结构体, 接下来再编写路由处理类, userv1_h.CheckUsers
在handler 文件夹中再创建一个userv1.go
| package handlers
import (
"github.com/gin-gonic/gin"
"yyxtest/dao"
)
type UserV1 struct {}
func (UserV1) CheckUsers(c *gin.Context) {
users := dao.GetAllUsers()
if users == nil{
c.JSON(200, gin.H{"error": 1, "msg": "查询失败"})
}
c.JSON(200, gin.H{"error":0, "msg":"查询成功", "users": users})
}
|
运行服务,如果没有什么意外的话将可以正常的请求到数据

关于MySQL的查询相关的语句可以参考我之前的文章 golang查询MySQL数据,
SQL注入的问题
如果在进行数据库的查询时,没有对用户的输入进校验,而是完全的进行字符串拼接,将会导致SQL注入的问题发生,如以下的代码
| func GetUser(name string) (user, error) {
sqlstr := fmt.Sprintf("select * from user_infos where `name`='%s'", name)
row := global.Mysql.QueryRow(sqlstr)
var u user
err := row.Scan(&u.ID, &u.Name, &u.Age)
if err != nil {
return user{}, err
}
return u, nil
}
|
sqlstr 直接进行字符串拼接将会导致注入问题。
正确的作法应该是使用点位符
| func GetUser(name string) (user, error) {
row := global.Mysql.QueryRow("select * from user_infos where name=?", name)
var u user
err := row.Scan(&u.ID, &u.Name, &u.Age)
if err != nil {
return user{}, err
}
return u, nil
}
|
插入数据
插入数据也比较简单,使用Exec函数 ,在userdao.go中写入以下代码
| func InsertUser(name string, age int) error {
exec, err := global.Mysql.Exec("INSERT into user_infos(name, age) values (?,?)", name, age)
if err != nil {
return err
}
_, err = exec.LastInsertId()
if err != nil {
return err
}
return nil
}
|
在处理类中写入以下代码
| func (UserV1) AddUser(c *gin.Context) {
name := c.Query("name")
age := c.Query("age")
age_i, err:=strconv.Atoi(age)
if err!=nil{
c.JSON(200, gin.H{"error": 1, "msg": "age 参数不正确"})
}else{
err = dao.InsertUser(name, age_i)
if err!= nil{
c.JSON(200, gin.H{"error": 2, "msg": err.Error()})
}else{
c.JSON(200, gin.H{"error": 1, "msg": "插入成功"})
}
}
}
|
MySQL的初步使用还是比较简单的,只是在初始化的时候定义好相应的对象,但是MySQL是个博大精深的数据库,入门很简单,但是想要深入还是需要很多的内功修炼的。