本文主要介绍使用gorm库连接和操作mysql,首先安装gorm和mysql依赖库;然后初始化mysql,配置连接池等基本信息;然后建表、完成dao、controller开发;最后在swagger中测试
文章目录
欢迎大家访问个人博客网址:https://www.maogeshuo.com,博主努力更新中...
参考文档:
前言
GORM 是一个强大的 ORM(对象关系映射)库,可以简化数据库操作并提供方便的查询方法。它提供了一种简单而强大的方式来处理数据库操作,包括连接到数据库、定义数据模型、执行查询、插入、更新和删除数据等功能。
以下是 GORM 库的一些主要特点和优点:
-
支持多种数据库引擎:GORM 支持多种主流的数据库引擎,如 MySQL、PostgreSQL、SQLite、SQL Server 等。
-
自动迁移:通过 GORM,你可以使用简单的代码就能自动创建、更新数据库表结构,而无需手动编写 SQL。
-
链式方法:GORM 提供了丰富的链式方法,用于构建复杂的查询条件,并支持预加载相关数据,实现数据的懒加载。
-
事务支持:GORM 提供了事务支持,确保在数据操作时的原子性和一致性。
-
回调函数:你可以注册各种回调函数,以在特定事件发生时执行自定义逻辑,如在保存数据之前或之后执行某些操作。
-
软删除:GORM 支持软删除功能,即标记删除数据而非真正从数据库中删除,方便数据恢复和数据保留。
-
关系映射:GORM 可以轻松地定义模型之间的关系,如一对一、一对多、多对多等,并提供方便的方法来处理这些关系。
-
性能优化:GORM 对数据库操作进行了优化,提供了缓存、批量插入等功能,以提高性能。
安装依赖库
当使用 GORM 时,首先需要安装 GORM 包和相应的数据库驱动。
go
go get -u gorm.io/gorm
go get -u gorm.io/driver/mysql
数据库初始化
初始化mysql,我这里只配置了两张表,设置连接池相关参数
go
package core
import (
"code-go/model/do"
"fmt"
"gorm.io/driver/mysql"
"gorm.io/gorm"
"log"
"time"
)
var DB *gorm.DB
// InitMysql 初始化mysql
func InitMysql() (*gorm.DB, error) {
mysqlConfig := Config.Database.Mysql
dsn := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=utf8mb4&parseTime=True&loc=Local",
mysqlConfig.UserName,
mysqlConfig.Password,
mysqlConfig.Host,
mysqlConfig.Port,
mysqlConfig.Database)
LOG.Println("dsn: ", dsn)
dbMysql, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
if err != nil {
log.Println("open db_mysql error ", err)
return nil, err
}
DB = dbMysql
//迁移表
autoMigrateTable()
// 是否打开日志
if mysqlConfig.LogMode {
dbMysql.Debug()
}
db, _ := dbMysql.DB()
//设置连接池的最大闲置连接数
db.SetMaxIdleConns(10)
//设置连接池中的最大连接数量
db.SetMaxOpenConns(100)
//设置连接的最大复用时间
db.SetConnMaxLifetime(10 * time.Second)
return dbMysql, nil
}
// 自动迁移表
func autoMigrateTable() {
err := DB.AutoMigrate(&do.User{}, &do.OperationLog{})
if err != nil {
LOG.Error("迁移表结构失败:", err)
}
}
SetMaxIdleConns :设置最大空闲连接数,目前默认值为2,0<= n<=MaxIdleConns
SetMaxOpenConns : 设置最大连接数,默认为0
SetConnMaxLifetime :设置连接的最大存活时间,当d<=0,connections are not closed due to a connection's age
至于其他的配置,查看源码和官方文档https://gorm.io/docs/
账号注册和登录
创建实体类User
go
package do
import (
"gorm.io/gorm"
)
type User struct {
gorm.Model
Username string `gorm:"type:varchar(20);not null;unique;index:idx_username" json:"username"`
Password string `gorm:"size:255;not null" json:"password,omitempty"`
Mobile string `gorm:"type:varchar(11);not null;unique" json:"mobile"`
Avatar string `gorm:"type:varchar(255)" json:"avatar"`
Nickname *string `gorm:"type:varchar(20)" json:"nickname"`
Introduction *string `gorm:"type:varchar(255)" json:"introduction"`
Status uint `gorm:"type:tinyint(1);default:1;comment:'1正常, 2禁用'" json:"status"`
Creator string `gorm:"type:varchar(20);" json:"creator"`
Roles []*Role `gorm:"many2many:user_roles" json:"roles"`
}
dao
go
package dao
import (
"code-go/core"
"code-go/model/do"
)
func InsertUser(user do.User) error {
tx := core.DB.Create(&user)
if tx.Error != nil {
core.LOG.Println("insert user in do fail")
return tx.Error
}
return nil
}
func GetUserByUsername(userName string) *do.User {
var user *do.User
tx := core.DB.Model(&do.User{}).Where("username=?", userName).Find(&user)
if tx.Error != nil {
core.LOG.Println("Query user by username fail")
}
return user
}
controller
go
package api
import (
"code-go/app/admin/dao"
"code-go/common"
"code-go/core"
"code-go/global"
"code-go/model/do"
"code-go/model/vo"
"code-go/util"
"fmt"
"github.com/gin-gonic/gin"
"net/http"
"strconv"
"time"
)
// Register 用户注册
//
// @Summary 用户注册
// @Produce json
// @Router /api/user/register [post]
// @Param username query string true "用户名"
// @Param password query string true "密码"
// @Param mobile query string true "电话"
func Register(c *gin.Context) {
userName := c.Query("username")
password := c.Query("password")
mobile := c.Query("mobile")
if userName == "" || password == "" {
core.LOG.Println("输入的用户名和密码为空")
c.JSON(http.StatusOK, common.FailWithCodeMsg(common.VALILD_FAIL, "输入的用户名或密码为空"))
return
}
// 查询用户
daoUser := dao.GetUserByUsername(userName)
if daoUser.Username == userName {
core.LOG.Printf("用户: %s 已存在\n", userName)
c.JSON(http.StatusOK, common.OkWithData("输入的用户已存在"))
return
}
// 生成用户
genPasswd := util.GenPasswd(password)
var user do.User
user.Mobile = mobile
user.Username = userName
user.Password = genPasswd
user.Status = core.User_status_OK
err := dao.InsertUser(user)
if err != nil {
core.LOG.Println("插入用户失败 ", err)
c.JSON(http.StatusOK, common.FailWithCodeMsg(common.INSERT_DB_FAIL, "插入用户失败"))
return
}
c.JSON(http.StatusOK, common.Ok())
}
// Login 用户登录
//
// @Summary 用户登录
// @Produce json
// @Router /api/user/login [post]
// @Param username query string true "用户名"
// @Param password query string true "密码"
func Login(c *gin.Context) {
userLogin := vo.UserLoginReqVo{}
err := c.ShouldBindQuery(&userLogin)
if err != nil {
core.LOG.Println("用户登录:参数绑定失败")
c.JSON(http.StatusOK, common.FailWithMsg("参数绑定失败"))
return
}
userName := userLogin.Username
password := userLogin.Password
if userName == "" || password == "" {
core.LOG.Println("用户登录:输入的用户名和密码为空")
c.JSON(http.StatusOK, common.FailWithCodeMsg(common.VALILD_FAIL, "输入的用户名和密码为空"))
return
}
// 校验格式
isMatched := util.ValidateUserName(userName)
if !isMatched {
core.LOG.Println("用户登录:用户名格式校验失败")
c.JSON(http.StatusOK, common.FailWithCodeMsg(common.VALILD_FAIL, "用户名格式校验失败"))
return
}
//isMatched = util.ValidatePassword(password)
//if !isMatched {
// global.Log.Println("用户登录:密码格式校验失败")
// c.JSON(http.StatusOK, common.FailWithCodeMsg(common.VALILD_FAIL, "密码格式校验失败"))
// return
//}
// TODO: 生成验证码
// 数据库查询
user := dao.GetUserByUsername(userName)
if user.Username == "" {
core.LOG.Println("用户登录:未查询到用户")
c.JSON(http.StatusOK, common.FailWithCodeMsg(common.USER_NOT_EXIST, common.GetMapInfo(common.USER_NOT_EXIST)))
return
}
//校验登录密码是否和数据库一致
isPasswordMatch := util.ComparePasswd(user.Password, password)
if !isPasswordMatch {
core.LOG.Println("用户登录:用户密码输入错误")
c.JSON(http.StatusOK, common.FailWithCodeMsg(common.USER_PASSWORD_NOT_MATCHED,
common.GetMapInfo(common.USER_PASSWORD_NOT_MATCHED)))
return
}
// TODO:生成token
token, err := util.GenerateToken(strconv.Itoa(int(user.ID)), user.Username)
if err != nil {
core.LOG.Println("用户登录:生成token失败")
c.JSON(http.StatusOK, common.FailWithCodeMsg(common.AUTHORIZATION_FAIL, "生成token失败"))
return
}
c.Header(global.Authorization, token)
// 写入redis
redisToken := fmt.Sprintf("user-token-%s", userName)
isOk := core.Redis.SetEX(redisToken, token, 7*24*time.Hour)
if isOk == false {
core.LOG.Println("用户登录:设置值到redis")
c.JSON(http.StatusOK, common.FailWithCodeMsg(common.REDIS_SET_FAIL, "设置值到Redis失败"))
return
}
userVo := vo.ConvertToUserResVo(user)
c.Header(global.Authorization, token)
c.JSON(http.StatusOK, gin.H{
"token": token,
"user": userVo,
})
}
// GetUserByUsername 根据用户名查询用户
//
// @Summary 用户登录
// @Produce json
// @Router /api/user/getUserByName [get]
// @Param username query string true "用户名" maxlength(20)
func GetUserByUsername(c *gin.Context) {
userName := c.Query("username")
user := dao.GetUserByUsername(userName)
if user.Username == "" {
c.JSON(http.StatusOK, common.FailWithMsg("未查询到该用户"))
return
}
userVo := vo.ConvertToUserResVo(user)
c.JSON(http.StatusOK, common.OkWithData(userVo))
}
// GetAllUser 查询所有的用户
//
// @Summary 查询所有的用户
// @Produce json
// @Router /api/user/getAllUser [get]
// @Param pagenum query int true "页数"
// @Param pagesize query int true "页面大小"
func GetAllUser(c *gin.Context) {
pageNum, _ := strconv.Atoi(c.Query("pagenum"))
pageSize, _ := strconv.Atoi(c.Query("pagesize"))
core.LOG.Printf("pagenum: %d, pagesize: %d\n", pageNum, pageSize)
if pageNum <= 0 {
pageNum = 0
}
if pageSize <= 0 {
pageSize = 1
}
users, total := dao.GetUser(pageNum, pageSize)
c.JSON(http.StatusOK, common.OkWithData(common.NewPageRes(users, total)))
}
router配置
go
package middleware
import (
"code-go/app/admin/api"
_ "code-go/docs"
"github.com/gin-gonic/gin"
swaggerFiles "github.com/swaggo/files" // swagger embed files
ginSwagger "github.com/swaggo/gin-swagger" // gin-swagger middleware
)
// InitRouter 初始化Router
func InitRouter() *gin.Engine {
g := gin.New()
g.Use(gin.Recovery())
g.Use(Logger())
g.Use(Cors())
// 需授权
auth := g.Group("/api")
{
auth.GET("/user/getAllUser", api.GetAllUser)
auth.POST("/user/register", api.Register)
auth.POST("/user/login", api.Login)
auth.GET("/user/getUserByName", api.GetUserByUsername)
}
// 无需授权
norm := g.Group("/")
{
norm.GET("/getIp", api.GetIp)
norm.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))
norm.GET("/captcha", api.GetCaptcha)
norm.POST("/checkCaptcha", api.CheckCaptcha)
}
return g
}
测试
使用swagger测试如下,可以确认登录成功,且有token生成