本文主要介绍使用gorm库连接和操作mysql,首先安装gorm和mysql依赖库;然后初始化mysql,配置连接池等基本信息;然后建表、完成dao、controller开发;最后在swagger中测试
文章目录
- 前言
- 安装依赖库
- 数据库初始化
- 账号注册和登录
- 创建实体类User
- dao
- controller
- router配置
- 测试
欢迎大家访问个人博客网址:https://www.maogeshuo.com,博主努力更新中…
参考文档:
- 【go语言开发】yaml文件配置和解析
- 【go语言开发】loglus日志框架的使用
- 【go语言开发】swagger安装和使用
前言
GORM 是一个强大的 ORM(对象关系映射)库,可以简化数据库操作并提供方便的查询方法。它提供了一种简单而强大的方式来处理数据库操作,包括连接到数据库、定义数据模型、执行查询、插入、更新和删除数据等功能。
以下是 GORM 库的一些主要特点和优点:
-
支持多种数据库引擎:GORM 支持多种主流的数据库引擎,如 MySQL、PostgreSQL、SQLite、SQL Server 等。
-
自动迁移:通过 GORM,你可以使用简单的代码就能自动创建、更新数据库表结构,而无需手动编写 SQL。
-
链式方法:GORM 提供了丰富的链式方法,用于构建复杂的查询条件,并支持预加载相关数据,实现数据的懒加载。
-
事务支持:GORM 提供了事务支持,确保在数据操作时的原子性和一致性。
-
回调函数:你可以注册各种回调函数,以在特定事件发生时执行自定义逻辑,如在保存数据之前或之后执行某些操作。
-
软删除:GORM 支持软删除功能,即标记删除数据而非真正从数据库中删除,方便数据恢复和数据保留。
-
关系映射:GORM 可以轻松地定义模型之间的关系,如一对一、一对多、多对多等,并提供方便的方法来处理这些关系。
-
性能优化:GORM 对数据库操作进行了优化,提供了缓存、批量插入等功能,以提高性能。
安装依赖库
当使用 GORM 时,首先需要安装 GORM 包和相应的数据库驱动。
go get -u gorm.io/gorm
go get -u gorm.io/driver/mysql
数据库初始化
初始化mysql,我这里只配置了两张表,设置连接池相关参数
package coreimport ("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.Mysqldsn := 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
package doimport ("gorm.io/gorm"
)type User struct {gorm.ModelUsername 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
package daoimport ("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.Usertx := 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
package apiimport ("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.Useruser.Mobile = mobileuser.Username = userNameuser.Password = genPasswduser.Status = core.User_status_OKerr := 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.Usernamepassword := userLogin.Passwordif 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:生成tokentoken, 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)// 写入redisredisToken := 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配置
package middlewareimport ("code-go/app/admin/api"_ "code-go/docs""github.com/gin-gonic/gin"swaggerFiles "github.com/swaggo/files" // swagger embed filesginSwagger "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生成