初识gin和gorm | 青训营笔记

175 阅读5分钟

这是我参与「第五届青训营 」伴学笔记创作活动的第 5 天

登录注册功能(纯数据库实现)

项目结构

├─.idea
├─controller
├─dao
├─database
├─main
├─model
├─router
├─service
├─test
└─utils

由上面可知,基本的结构主要就是由路由层(router),控制层(controller),服务层(service),数据访问层(dao),数据模型层(model)构成。 在这里与原本JAVA后端的区别是,service层就直接实现具体业务代码,JAVA中还有Impl层。 。

代码

database/mysql.go

连接数据库,这里可以当成是数据库的配置之类的,以"数据库账户:数据库密码@(IP地址:端口号)/数据库名?timeout=10s&readTimeout=30s&writeTimeout=60s"的形式填入自己对应的数据。 此处定义了一个全局变量以及一个初始化数据库的函数,注意在main函数需要初始化一次数据库,即调用database.SqlClient() ,否则数据库就不能用

package database  
  
import (  
   "gorm.io/driver/mysql"  
   "gorm.io/gorm"   
   "reflect")  
  
/**  
 * @Description:全局 DB */
 var (  
   SqlDB *gorm.DB  
)  
  
/**  
 * @Description: 初始化数据库  
 * @return *gorm.DB  
 */
 func SqlClient() *gorm.DB {  
   if SqlDB == nil || reflect.DeepEqual(SqlDB, gorm.DB{}) {  
      // 声明连接字符串  
      dsn := "数据库账户:数据库密码@(IP地址:端口号)/数据库名?timeout=10s&readTimeout=30s&writeTimeout=60s"  
      // 开启连接  
      db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})  
      if err != nil {  
         //panic("failed to connect database")  
      }  
      SqlDB = db  
      return db  
   }  
   return SqlDB  
}

model/user.go

这里是数据模型层,这里可以先在数据库定义好结构,然后用XO工具生成model层的数据,注意这里实体类的变量首字母要大写,否则会不成功;这里还有一个函数是用来返回表名的。func (u *User) TableName() string即为User类返回string类型的意思

补充:

数据库设计部分规范

【强制】id类型没有特殊要求,必须使用bigint unsigned,禁止使用int,即使现在的数据量很小。id如果是数字类型的话,必须是8个字节。

【推荐】字段尽量设置为 NOT NULL, 为字段提供默认值。 如字符型的默认值为一个空字符值串’’;数值型默认值为数值 0;逻辑型的默认值为数值 0;

【推荐】每个字段和表必须提供清晰的注释

【推荐】时间统一格式:‘YYYY-MM-DD HH:MM:SS’

【强制】表达是与否概念的字段,必须使用 is_xxx 的方式命名,数据类型是 unsigned tinyint ( 1表示是,0表示否)。
说明:任何字段如果为非负数,必须是 unsigned。
正例:表达逻辑删除的字段名 is_deleted,1 表示删除,0 表示未删除。

【强制】表名、字段名必须使用小写字母或数字,禁止出现数字开头,禁止两个下划线中间只 出现数字。数据库字段名的修改代价很大,因为无法进行预发布,所以字段名称需要慎重考虑。 说明:MySQL 在 Windows 下不区分大小写,但在 Linux 下默认是区分大小写。因此,数据库 名、表名、字段名,都不允许出现任何大写字母,避免节外生枝。 正例:health_user,rdc_config,level3_name 反例:HealthUser,rdcConfig,level_3_name

【强制】表名不使用复数名词。 说明:表名应该仅仅表示表里面的实体内容,不应该表示实体数量,对应于 DO 类名也是单数 形式,符合表达习惯。

package model  
  
import (  
   "database/sql"  
   "time")  
  
// 实体类  
type User struct {  
   UserID        uint64        `json:"user_id"`        // user_id  
   Username      string        `json:"username"`       // username  
   Password      string        `json:"password"`       // password  
   CreateTime    time.Time     `json:"create_time"`    // create_time  
}  
  
// 表名  
func (u *User) TableName() string {  
   return "tb_users"  
}

dao/userMapper.go

数据访问层(dao),这里是与数据库连接的,相当于JAVA注解或者XML写的sql语句,这里还是直接理解成一个类即可,需要有初始化函数NewUserMapper(),下面是基本增删查改的格式

补充

常用gorm操作

// 查询所有的记录
db.Find(&users)
//// SELECT * FROM users;
db.Where("name = ?", "jinzhu").First(&user)
db.Limit(3).Find(&users)
//// SELECT * FROM users LIMIT 3;
db.Offset(3).Find(&users)
//// SELECT * FROM users OFFSET 3;
package dao  
  
import (  
   "go_douyin/database"  
   "go_douyin/model")  
  
// UserMapper 自定义UserMapper的类型,于user实体类对应即可  
type UserMapper struct{}  
  
func NewUserMapper() *UserMapper {  
   return &UserMapper{}  
}  
  
func (UserMapper) Login(username string, password string) int64 {  
   var users []model.User  
   //查询数据  
   res := database.SqlDB.Where("username = ?", username).Where("password = ?", password).Find(&users)  
   return res.RowsAffected  
}  
  
func (UserMapper) FindAll() []model.User {  
   var users []model.User  
   //查询数据  
   database.SqlDB.Find(&users)  
   return users  
}  
  
func (UserMapper) Add(user model.User) int64 {  
   //新增数据  
   res := database.SqlDB.Create(&user)  
   return res.RowsAffected  
}  
  
func (UserMapper) Update(user model.User) int64 {  
   //更新数据  
   res := database.SqlDB.Save(user)  
   return res.RowsAffected  
}  
  
func (UserMapper) Delete(user model.User) int64 {  
   //删除数据  
   res := database.SqlDB.Delete(&model.User{}, user.UserID)  
   return res.RowsAffected  
}

service/userService.go

服务层(service),注意这里初始化的同时,还初始化了mapper的,而且定义结构的时候,也多写了一个userMapper(可以理解成JAVA的注解另一个层),这里是直接写业务逻辑的

package service  
  
import (  
   "go_douyin/dao"  
   "go_douyin/model"   "time")  
  
type UserService struct {  
   userMapper *dao.UserMapper  
}  
  
func NewUserService() *UserService {  
   return &UserService{  
      userMapper: dao.NewUserMapper(),  
   }  
}  
  
func (h *UserService) Register(user model.User) bool {  
   user.CreateTime = time.Now()  
   row := h.userMapper.Add(user)  
   if row > 0 {  
      return true  
   } else {  
      return false  
   }  
}  
  
func (h *UserService) Login(username string, password string) bool {  
   row := h.userMapper.Login(username, password)  
   if row > 0 {  
      return true  
   } else {  
      return false  
   }  
}

controller/userController.go

控制层,这里后面可以换成统一格式返回,c.BindJSON(&user)可以直接当成是绑定POST请求过来的数据,与实体类一一对应,这里的注意同上

package controller  
  
import (  
   "github.com/gin-gonic/gin"  
   "go_douyin/model"   
   "go_douyin/service"   
   "net/http")  
  
type UserController struct {  
   UserService *service.UserService  
}  
  
func NewUserController() *UserController {  
   return &UserController{UserService: service.NewUserService()}  
}  
  
func (h *UserController) Register(c *gin.Context) {  
   var user model.User  
   c.BindJSON(&user)  
   if h.UserService.Register(user) {  
      c.JSON(http.StatusOK, gin.H{"code": "200", "msg": "OK", "data": "注册成功"})  
   } else {  
      c.JSON(http.StatusBadRequest, gin.H{"code": "500", "msg": "OK", "data": "注册失败"})  
   }  
}  
  
func (h *UserController) Login(c *gin.Context) {  
   var user model.User  
   c.BindJSON(&user)  
   if h.UserService.Login(user.Username, user.Password) {  
      c.JSON(http.StatusOK, gin.H{"code": "200", "msg": "OK", "data": "登录成功"})  
   } else {  
      c.JSON(http.StatusBadRequest, gin.H{"code": "500", "msg": "OK", "data": "登录失败"})  
   }  
}

router/router.go

路由层,以以下的格式进行,注意这里还需要将controller初始化一次,然后以Group的形式定义分组路由

package router  
  
import (  
   "github.com/gin-gonic/gin"  
   "go_douyin/controller")  
  
func SetupRouter() *gin.Engine {  
   router := gin.Default()  
   userController := controller.NewUserController()  
   v1 := router.Group("/douyin/user")  
   {  
      v1.POST("register", userController.Register)  
      v1.POST("login", userController.Login)  
   }  
   return router  
}

main.go

这里是运行的主函数,需要初始化数据库和初始化一下路由(让路由生效),以及定义一下端口

package main  
  
import (  
   "go_douyin/database"  
   "go_douyin/router")  
  
func main() {  
   // 注意初始化数据库  
   database.SqlClient()  
   r := router.SetupRouter()  
   r.Run(":8081")  
}