gin: 用gorm连接到mysql数据库

一,安装用到的第三方库

官网地址:

https://gorm.io/

安装gorm:

$ go get -u gorm.io/gorm
go: downloading gorm.io/gorm v1.30.5
go: downloading golang.org/x/text v0.29.0
go: added github.com/jinzhu/inflection v1.0.0
go: added github.com/jinzhu/now v1.1.5
go: upgraded golang.org/x/text v0.28.0 => v0.29.0
go: added gorm.io/gorm v1.30.5

安装数据库驱动:

$ go get -u gorm.io/driver/mysql
go: downloading gorm.io/driver/mysql v1.6.0
go: downloading github.com/go-sql-driver/mysql v1.9.3
go: added filippo.io/edwards25519 v1.1.0
go: added github.com/go-sql-driver/mysql v1.9.3
go: added gorm.io/driver/mysql v1.6.0

二,编写代码

1,自定义日志类:

package global

import (
	"bytes"
	"context"
	"errors"
	"fmt"
	"github.com/gin-gonic/gin"
	gormlogger "gorm.io/gorm/logger"
	"gorm.io/gorm/utils"
	"log"
	"os"
	"runtime"
	"strings"
	"time"
)

const (
	Reset       = "\033[0m"
	Red         = "\033[31m"
	Green       = "\033[32m"
	Yellow      = "\033[33m"
	Blue        = "\033[34m"
	Magenta     = "\033[35m"
	Cyan        = "\033[36m"
	White       = "\033[37m"
	BlueBold    = "\033[34;1m"
	MagentaBold = "\033[35;1m"
	RedBold     = "\033[31;1m"
	YellowBold  = "\033[33;1m"
)

const (
	// Silent silent log level
	Silent gormlogger.LogLevel = iota + 1
	// Error error log level
	Error
	// Warn warn log level
	Warn
	// Info info log level
	Info
)

//gorm原日志的配置
type FileLogConfig struct {
	gormlogger.Config
}

//定义日志类
type StdFileLogger struct {
	FileLogConfig
	infoStr, warnStr, errStr            string
	traceStr, traceErrStr, traceWarnStr string
	path,name string
}

//初始化,区分有颜色和无颜色
func NewStdFileLogger(config FileLogConfig,path,name string) *StdFileLogger {
	var (
		infoStr      = "%s\n[info] "
		warnStr      = "%s\n[warn] "
		errStr       = "%s\n[error] "
		traceStr     = "%s\n[%.3fms] [rows:%v] %s"
		traceWarnStr = "%s %s\n[%.3fms] [rows:%v] %s"
		traceErrStr  = "%s %s\n[%.3fms] [rows:%v] %s"
	)

	if config.Colorful {
		infoStr = Green + "%s\n" + Reset + Green + "[info] " + Reset
		warnStr = BlueBold + "%s\n" + Reset + Magenta + "[warn] " + Reset
		errStr = Magenta + "%s\n" + Reset + Red + "[error] " + Reset
		traceStr = Green + "%s\n" + Reset + Yellow + "[%.3fms] " + BlueBold + "[rows:%v]" + Reset + " %s"
		traceWarnStr = Green + "%s " + Yellow + "%s\n" + Reset + RedBold + "[%.3fms] " + Yellow + "[rows:%v]" + Magenta + " %s" + Reset
		traceErrStr = RedBold + "%s " + MagentaBold + "%s\n" + Reset + Yellow + "[%.3fms] " + BlueBold + "[rows:%v]" + Reset + " %s"
	}

	return &StdFileLogger{
		FileLogConfig:    config,
		path:path,
		name:name,
		//Loggers:      loggers,
		infoStr:      infoStr,
		warnStr:      warnStr,
		errStr:       errStr,
		traceStr:     traceStr,
		traceWarnStr: traceWarnStr,
		traceErrStr:  traceErrStr,
	}
}

func (logger *StdFileLogger) printf(msg string, data ...interface{}) {
	//得到要打印的内容
	content:=fmt.Sprintf(msg, data...)
	//打印到控制台
	log.Printf(content)
	//得到文件名:
	now := time.Now()
	dateStr:=now.Format("2006-01-02")
	filePath := logger.path+"/"+logger.name+"_"+dateStr+".log"

	// 替换掉彩色打印符号
	content = strings.ReplaceAll(content, Reset, "")
	content = strings.ReplaceAll(content, Red, "")
	content = strings.ReplaceAll(content, Green, "")
	content = strings.ReplaceAll(content, Yellow, "")
	content = strings.ReplaceAll(content, Blue, "")
	content = strings.ReplaceAll(content, Magenta, "")
	content = strings.ReplaceAll(content, Cyan, "")
	content = strings.ReplaceAll(content, White, "")
	content = strings.ReplaceAll(content, BlueBold, "")
	content = strings.ReplaceAll(content, MagentaBold, "")
	content = strings.ReplaceAll(content, RedBold, "")
	content = strings.ReplaceAll(content, YellowBold, "")

	// 格式化时间
	formatted := now.Format("2006-01-02 15:04:05")
	content = formatted+" "+content
	//保存到文件
	logger.LogToFile(filePath,content+"\n")
}

//日志的级别
func (logger *StdFileLogger) LogMode(lv gormlogger.LogLevel) gormlogger.Interface {
	logger.LogLevel = lv
	return logger
}

//info
func (logger *StdFileLogger) Info(ctx context.Context, msg string, data ...interface{}) {
	if logger.LogLevel >= Info {
		logger.printf(logger.infoStr+msg, append([]interface{}{utils.FileWithLineNum()}, data...)...)
	}
}

//warn
func (logger *StdFileLogger) Warn(ctx context.Context, msg string, data ...interface{}) {
	if logger.LogLevel >= Warn {
		logger.printf(logger.infoStr+msg, append([]interface{}{utils.FileWithLineNum()}, data...)...)
	}
}

//error
func (logger *StdFileLogger) Error(ctx context.Context, msg string, data ...interface{}) {
	if logger.LogLevel >= Error {
		//fmt.Println("当前是错误日志:")
		logger.printf("当前是错误日志:\n")
		logger.printf(logger.infoStr+msg, append([]interface{}{utils.FileWithLineNum()}, data...)...)
	}
}

// Trace 打印sql语句
func (logger *StdFileLogger) Trace(ctx context.Context, begin time.Time, fc func() (sql string, rowsAffected int64), err error) {
	if logger.LogLevel <= Silent {
		return
	}

	fmt.Println("gorm trace===============")
	fmt.Println(ctx)
	//fmt.Println("==================create:create======================")
	c, ok := ctx.(*gin.Context)
	if ok {
		fmt.Println("转换成功")

		envStr:=GetHttpEnv(c)
		fmt.Println("envstr:")
		fmt.Println(envStr)
		logger.printf(envStr)
	} else {
		fmt.Println("转换出错")
	}

	elapsed := time.Since(begin)
	switch {
	case err != nil && logger.LogLevel >= Error && (!errors.Is(err, gormlogger.ErrRecordNotFound) || !logger.IgnoreRecordNotFoundError):
		sql, rows := fc()
		//得到stack
		stack:= logger.PrintStackTrace(err)
		if rows == -1 {
			logger.printf(logger.traceErrStr+"\n"+stack, utils.FileWithLineNum(), err, float64(elapsed.Nanoseconds())/1e6, "-", sql)
		} else {
			logger.printf(logger.traceErrStr+"\n"+stack, utils.FileWithLineNum(), err, float64(elapsed.Nanoseconds())/1e6, rows, sql)
		}
	case elapsed > logger.SlowThreshold && logger.SlowThreshold != 0 && logger.LogLevel >= Warn:
		sql, rows := fc()
		slowLog := fmt.Sprintf("SLOW SQL >= %v", logger.SlowThreshold)
		if rows == -1 {
			logger.printf(logger.traceWarnStr, utils.FileWithLineNum(), slowLog, float64(elapsed.Nanoseconds())/1e6, "-", sql)
		} else {
			logger.printf(logger.traceWarnStr, utils.FileWithLineNum(), slowLog, float64(elapsed.Nanoseconds())/1e6, rows, sql)
		}
	case logger.LogLevel == Info:
		sql, rows := fc()
		if rows == -1 {
			logger.printf(logger.traceStr, utils.FileWithLineNum(), float64(elapsed.Nanoseconds())/1e6, "-", sql)
		} else {
			logger.printf(logger.traceStr, utils.FileWithLineNum(), float64(elapsed.Nanoseconds())/1e6, rows, sql)
		}
	}
}

//出错时打印错误的堆栈信息
func (logger *StdFileLogger) PrintStackTrace(err error) string {
	// 创建一个缓冲区用于存储堆栈信息
	buf := bytes.NewBuffer(nil)

	// 获取当前goroutine的堆栈信息
	for i := 0; ; i++ {
		pc, file, line, ok := runtime.Caller(i)
		if !ok {
			break
		}

		fmt.Fprintf(buf, "%d: %s:%d (0x%x)\n", i, file, line, pc)
	}

	// 打印堆栈信息
	//fmt.Println(buf.String())
	return buf.String()
}

//写内容到文件
func (logger *StdFileLogger) LogToFile(filename,msg string) {
	// 输出到文件
	file, err := os.OpenFile(filename, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
	if err != nil {
		fmt.Println("日志文件的打开错误 :", err)
	}
	defer file.Close()
	if _, err := file.WriteString(msg); err != nil {
		fmt.Println("写入日志文件错误 :", err)
	}
}

2, 建立连接:

package global

import (
    "fmt"
    "gorm.io/driver/mysql"
    "gorm.io/gorm"
    "gorm.io/gorm/logger"
	"mediabank/config"
	"time"
)

var (
	DBLink *gorm.DB
)

func SetupDBLink() (error) {
	var err error

	//创建日志类
	path:="./logs"
	name:="gormlog"
	mylogger := NewStdFileLogger(FileLogConfig{
		Config: logger.Config{  // gorm日志原始配置项
			SlowThreshold:             200 * time.Millisecond,
			IgnoreRecordNotFoundError: true,
			Colorful:                  true,
		},
	},path,name).LogMode(Info)


	dsn := fmt.Sprintf("%s:%s@tcp(%s)/%s?charset=%s&parseTime=%t&loc=Local",
		config.DatabaseSetting.UserName,
		config.DatabaseSetting.Password,
		config.DatabaseSetting.Host,
		config.DatabaseSetting.DBName,
		config.DatabaseSetting.Charset,
		true, )

	//DBLink, err = gorm.Open(mysql.Open(dsn), &gorm.Config{Logger: logger.Default.LogMode(logger.Info),})
	DBLink, err = gorm.Open(mysql.Open(dsn),  &gorm.Config{Logger: mylogger,})
	if err != nil {
		return err
	}

	sqlDB, _ := DBLink.DB()

	// SetMaxIdleConns 设置空闲连接池中连接的最大数量
	sqlDB.SetMaxIdleConns(10)
	// SetMaxOpenConns 设置打开数据库连接的最大数量
	sqlDB.SetMaxOpenConns(30)
	// SetConnMaxLifetime 设置了连接可复用的最大时间
	sqlDB.SetConnMaxLifetime(time.Hour)
	//"query:before"
	return nil
}

3,初始化:

package main

import (
    "embed"
    "fmt"
    "log"
    "mediabank/config"
    "mediabank/global"
    "mediabank/routes"
)

// 嵌入文件只能为源码文件同级目录和子目录下的文件
//go:embed static/* templates/*
var embedFs embed.FS

func init() {
    //读取配置
    err := config.ReadSettings()
    if err != nil {
        log.Fatalf("init.setupSetting err: %v", err)
    }

    //创建到数据库的连接
    errDb := global.SetupDBLink()
    if errDb != nil {
        fmt.Println("db link error")
    }

    //创建访问日志
    errALog := global.SetupAccessLogger()
    if errALog != nil {
        fmt.Println("access log error")
    }

}


// 入口函数
func main() {

    //引入路由
    r := routes.Routes(embedFs)

    //run
    err := r.Run(":"+config.AppSetting.Port)
    if err != nil {
        return
    }
}

4, model

package model

type LinkModel struct {
	Id uint `gorm:"column:id;primaryKey" json:"id"`
	//定义链接的struct
	Cname string `gorm:"column:cname;" json:"cname"`
	Name string `gorm:"column:name;" json:"name"`
	Imageurl string `gorm:"column:imageurl;" json:"imageurl"`
	Linkurl string `gorm:"column:linkurl;" json:"linkurl"`

	DispImageurl string `gorm:"-;default:''" json:"dispimageurl"`

}

func (LinkModel) TableName() string {
	return "zr_link"
}

5, dao

package dao

import (
	"database/sql"
	"fmt"
	"github.com/gin-gonic/gin"
	"mediabank/global"
	"mediabank/model"
)


//按id查询 link
func GetHomeSlides(c *gin.Context) ([]*model.LinkModel, error) {

	fields := []string{"id", "cname", "name","imageurl","linkurl"}

	var rows *sql.Rows
	var err error

	rows,err = global.DBLink.WithContext(c).Select(fields).Table(model.LinkModel{}.TableName()).Where("cid=72 and status=1").Order("orderid desc,id desc").Offset(0).Limit(5).Rows()

	if err != nil {
		fmt.Println(err)
	}

	defer rows.Close()
	var links []*model.LinkModel
	for rows.Next() {
		r := &model.LinkModel{}
		if err := rows.Scan(&r.Id, &r.Cname, &r.Name,  &r.Imageurl, &r.Linkurl); err != nil {
			fmt.Println(err)
		} else {
			r.DispImageurl = ""
		}

		links = append(links, r)
	}

	return links, nil
}

6, 调用:

//得到列表
func (ic *MediaController) List(c *gin.Context) {

	//linkDao := dao.NewLinkDao(c)
	//_,err:=articleService.InsertOneImage(imageOne)

	slides,err := dao.GetHomeSlides(c)
	if err != nil {
		global.NewResult(c).ErrorCode(500,"数据查询出错",nil)
		return
	}
	fmt.Print("slides:")
	fmt.Print(slides)
	for index, value := range slides {
		fmt.Printf("Index: %d, Value id: %d\n", index, value.Id)
	}

	var data = gin.H{
		"Title": "Gin 列表示例",
		"Message": "欢迎来到Gin 列表!",
	}
	c.HTML(200, "list.html", data)
}

三,测试效果:

正常获取到数据后的结果:

2025/09/09 17:45:14 /data/goapp/mediabank/dao/linkDao.go:20
[31.745ms] [rows:-] SELECT id,cname,name,imageurl,linkurl FROM `zr_link` WHERE cid=72 and status=1 ORDER BY orderid desc,id desc LIMIT 5
slides:[0xc0001316e0 0xc000131740 0xc0001317a0 0xc000131800 0xc000131860]
Index: 0, Value id: 564
Index: 1, Value id: 563
Index: 2, Value id: 562
Index: 3, Value id: 561
Index: 4, Value id: 472

遇到报错时的日志:

2025-09-09 17:39:17 192.168.219.1 [2025-09-09 17:39:17] GET /media/list?a=1&b=2 HTTP/1.1 192.168.219.3:8080 Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36
GET: key:a,value:1
key:b,value:2

2025-09-09 17:39:17 /data/goapp/mediabank/dao/linkDao.go:20 Error 1054 (42S22): Unknown column 'cdid' in 'where clause'
[1.742ms] [rows:-] SELECT id,cname,name,imageurl,linkurl FROM `zr_link` WHERE cdid=72 and status=1 ORDER BY orderid desc,id desc LIMIT 5
0: /data/goapp/mediabank/global/gormlogger.go:208 (0xa98824)
1: /data/goapp/mediabank/global/gormlogger.go:177 (0xa98377)
2: /data/gopath/pkg/mod/gorm.io/gorm@v1.30.5/callbacks.go:140 (0x8ca3d0)
3: /data/gopath/pkg/mod/gorm.io/gorm@v1.30.5/finisher_api.go:518 (0x8d4ab1)
4: /data/goapp/mediabank/dao/linkDao.go:20 (0xa993ed)
5: /data/goapp/mediabank/controller/MediaController.go:52 (0xa99949)
6: /data/gopath/pkg/mod/github.com/gin-gonic/gin@v1.10.1/context.go:185 (0xa7e98a)
7: /data/goapp/mediabank/middleware/accesslog.go:54 (0xa9c4fe)
8: /data/gopath/pkg/mod/github.com/gin-gonic/gin@v1.10.1/context.go:185 (0xa9b27b)
9: /data/goapp/mediabank/routes/routes.go:128 (0xa9b269)
10: /data/gopath/pkg/mod/github.com/gin-gonic/gin@v1.10.1/context.go:185 (0xa8c74e)
11: /data/gopath/pkg/mod/github.com/gin-gonic/gin@v1.10.1/recovery.go:102 (0xa8c73b)
12: /data/gopath/pkg/mod/github.com/gin-gonic/gin@v1.10.1/context.go:185 (0xa8b824)
13: /data/gopath/pkg/mod/github.com/gin-gonic/gin@v1.10.1/logger.go:249 (0xa8b80b)
14: /data/gopath/pkg/mod/github.com/gin-gonic/gin@v1.10.1/context.go:185 (0xa8acaa)
15: /data/gopath/pkg/mod/github.com/gin-gonic/gin@v1.10.1/gin.go:644 (0xa8a667)
16: /data/gopath/pkg/mod/github.com/gin-gonic/gin@v1.10.1/gin.go:600 (0xa8a18c)
17: /usr/local/soft/go/src/net/http/server.go:3340 (0x7c59ad)
18: /usr/local/soft/go/src/net/http/server.go:2109 (0x7b6544)
19: /usr/local/soft/go/src/runtime/asm_amd64.s:1693 (0x484f80)

 

posted @ 2025-09-20 09:52  刘宏缔的架构森林  阅读(29)  评论(0)    收藏  举报