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) //設(shè)置空閑時間,這個是比mysql 主動斷開的時候短
conn.SetMaxOpenConns(10)
conn.SetMaxIdleConns(10)
return conn
}
}

sql.Open 方法開始連接數(shù)據(jù)庫,第一個參數(shù)為所要連接的數(shù)據(jù)庫類型。第二個參數(shù)為dsn,可以理解為連接數(shù)據(jù)庫的參數(shù)信息。

這里在連接以后,又對連接進行了三個設(shè)置。

conn.SetConnMaxLifetime 為設(shè)置連接的空閑時間,為什么要設(shè)置這個參數(shù)以及該設(shè)置多少是有講究的。

有的MySQL服務(wù)器為了性能考慮,會設(shè)置主動斷開空閑連接的,默認8個小時,但是一般的dba不會設(shè)置那么長,很有可能會設(shè)置10秒或者更短,所以這個參數(shù)要設(shè)置的更短,這個參數(shù)可以登錄MySQL 服務(wù)器執(zhí)行show global variables like '%timeout%'; 來查看,有個wait_timeout 值,這里的值要設(shè)置比這個值更短。

conn.SetMaxOpenConns(10) 和conn.SetMaxOpenConns(10) 這兩個方法會初始化一個連接池,保證池子里會有足夠的連接,這個值要設(shè)置多少需要根據(jù)應(yīng)用的并發(fā)情況。

但是SetMaxIdleConns 的值不要小于SetMaxOpenConns 設(shè)置的值,否則會導(dǎo)致頻繁的創(chuàng)建連接。

官方的解釋為

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" 這行代碼不能缺,該代碼會執(zhí)行一下init方法,初始化一些變量,如果沒有這行的話,會由于一些變量未被初始化而panic.

一般情況下,我們會將數(shù)據(jù)庫連接對象放到一個全局的變量中,然后將不同的數(shù)據(jù)庫操作封裝到dao中。

在項目目錄下再創(chuàng)建一個global目錄,再創(chuàng)建一個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 對象, 之后我們創(chuàng)建一個dao文件夾,再創(chuàng)建一個userdao.go文件,這里封裝數(shù)據(jù)庫的查詢. 先寫一個查詢所有記錄的函數(shù)。

事先準備一個表,并插入幾條數(shù)據(jù)

idnameage
1楊彥星18
2飯團兒1
3aaa100
4bbb22
5ccc19
635
746

數(shù)據(jù)查詢

在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 函數(shù)執(zhí)行select * from user_infos, 并且將該查詢結(jié)果返回[]user 的結(jié)構(gòu)體, 接下來再編寫路由處理類, userv1_h.CheckUsers

在handler 文件夾中再創(chuàng)建一個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})
}

運行服務(wù),如果沒有什么意外的話將可以正常的請求到數(shù)據(jù)

SQL注入的問題

如果在進行數(shù)據(jù)庫的查詢時,沒有對用戶的輸入進校驗,而是完全的進行字符串拼接,將會導(dǎo)致SQL注入的問題發(fā)生,如以下的代碼

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 直接進行字符串拼接將會導(dǎo)致注入問題。

正確的做法應(yīng)該是使用點位符

插入數(shù)據(jù)

插入數(shù)據(jù)也比較簡單,使用Exec函數(shù) ,在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 參數(shù)不正確"})
}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的初步使用還是比較簡單的,只是在初始化的時候定義好相應(yīng)的對象,但是MySQL是個博大精深的數(shù)據(jù)庫,入門很簡單,但是想要深入還是需要很多的內(nèi)功修煉的,之后再深入一些事務(wù)相關(guān)的查詢。

文章轉(zhuǎn)自微信公眾號@序語程言

上一篇:

在 .NET 和 Python 中創(chuàng)建了相同的 API — 哪個性能更好

下一篇:

使用FastAPI與aiohttp進行SSE響應(yīng)開發(fā)
#你可能也喜歡這些API文章!

我們有何不同?

API服務(wù)商零注冊

多API并行試用

數(shù)據(jù)驅(qū)動選型,提升決策效率

查看全部API→
??

熱門場景實測,選對API

#AI文本生成大模型API

對比大模型API的內(nèi)容創(chuàng)意新穎性、情感共鳴力、商業(yè)轉(zhuǎn)化潛力

25個渠道
一鍵對比試用API 限時免費

#AI深度推理大模型API

對比大模型API的邏輯推理準確性、分析深度、可視化建議合理性

10個渠道
一鍵對比試用API 限時免費