• 基于Gin+Gorm框架搭建MVC模式的Go语言企业级后端系统


    文/朱季谦

    环境准备:安装Gin与Gorm

    本文搭建准备环境:Gin+Gorm+MySql。

    Gin是Go语言的一套WEB框架,在学习一种陌生语言的陌生框架,最好的方式,就是用我们熟悉的思维去学。作为一名后端Java开发,在最初入门时,最熟悉的莫过于MVC分层结构,可以简单归纳成controller层,model层,dao层,而在SpringBoot框架里,大概也经常看到以下的分层结构——

    image

    这个结构分为java根目录与resources资源目录。

    在学习Go语言的Gin框架时,是否也可以参照这样的分层结构来搭建一套简单的后端系统呢。

    答案是,肯定的。

    接下来,我们就按照这个MVC分层结构,搭建一套基于Gin+Gorm框架的Go语言后端。

    搭建之前,先简单介绍一下Gin和Gorm分别是什么。

    Gin是一个golang的WEB框架,很轻量,依赖到很少,有些类似Java的SpringMVC,通过路由设置,可以将请求转发到对应的处理器上。

    Gorm是Go语言的ORM框架,提供一套对数据库进行增删改查的接口,使用它,就可以类似Java使用Hibernate框架一样,可对数据库进行相应操作。

    若要用到这两套框架,就需要import依赖进来,依赖进来前,需要Go命令安装Gin和Gorm。

    go get -u github.com/gin-gonic/gin
    go get -u github.com/jinzhu/gorm
    

    最好放在GOPATH目录下。

    我的GOPATH目录在C:UsersAdministratorgo下:
    image

    通过Go命令安装的依赖包放在这个目录底下C:UsersAdministratorgosrc下:
    image

    现在,我们就参考SpringBoot的分层结构,搭建一套MVC分层结构系统。

    一、搭建根目录与资源目录。

    先创建一个Go项目,这里,我取名为go-admin,底下创建一个go目录,用于存放Go代码;一个resources资源目录,存放配置文件,结构如下——
    image
    go根目录底下,创建controller、service、dao、entity包,另外,还需要一个router包,用于存放路由文件,可能你对路由文件不是很理解,那么,你可以简单理解为,这个路由的作用,就类似SpringMVC的@RequestMapping("/user")和@GetMapping("/list")注解组合起到的作用,通过路由,就可以找到需要调用的后端方法。创建完这些包后,若在SpringBoot项目里,是否还缺少一个xxxxxApplication.java的启动类,没错,在Go里,同样需要一个启动类,该启动类文件可以直接命名为main.go。

    创建以上包与类后,go根目录底下结构如下:
    image

    接下来,是在resources资源目录创建一个application.yaml配置文件,当然,这个配置文件可以随便命名,不用像SpringBoot那样需要考虑其命名背后所代表的优先级。
    image
    这个配置文件里,就存放需要用到的Mysql数据库连接信息:

    url: 127.0.0.1
    userName: root
    password: root
    dbname: example
    post: 3306
    

    这些基础工作做好后,就可以填充代码了。


    二、dao层的搭建。

    在dao层下,建立一个mysql.go文件,这个文件在dao的包下,最初的效果如下
    image

    按照以往jdbc连接数据库的步骤,首先需要加载jdbc驱动程序,然后再创建数据库的连接,其实,在Go连接数据库,同样需要类似这样的操作。

    首先,需要导入数据库驱动程序。

    Gorm已经包含了驱动程序,只需要将它导入进来即可:

    import _ "github.com/jinzhu/gorm/dialects/mysql"
    

    进入到这个依赖包的源码,根据命名就可以看到出,这是一个go语言的mysql驱动包——
    image

    除此之外,还提供了mssql、postgres、sqlite的驱动包。

    底层使用到的是GORM 框架,自然也要把它依赖进来:

    import  "github.com/jinzhu/gorm"
    

    另外,还需要依赖以下几个包,用于读取yaml配置文件数据与拼接成url字符串:

    import "io/ioutil"
    import "gopkg.in/yaml.v2"
    import "fmt"
    

    当依赖的包过多时,我们可以统一放到一个()号里,例如这样:

    import (
      "github.com/jinzhu/gorm"
       _ "github.com/jinzhu/gorm/dialects/mysql"
       "io/ioutil"
       "gopkg.in/yaml.v2"
       "fmt"
    )
    

    到这一步,效果如下:
    image

    这里爆红色是正常的,Go语言与Java不同的一个地方是,若依赖进来的包,没有被用到话,会直接出现红色异常提示,后面写到用到它们的代码时,就正常了。

    接下来,定义一个用于接收yaml配置参数的struct结构体,你可以简单将它理解为Java的类。

    type conf struct {
       Url string `yaml:"url"`
       UserName string `yaml:"userName"`
       Password string `yaml:"password"`
       DbName string `yaml:"dbname"`
       Port string `yaml:"post"`
    }
    

    然后提供一个读取解析该yaml配置的方法,将读取到的配置参数数据转换成上边的结构体conf

    func (c *conf) getConf() *conf {
    	//读取resources/application.yaml文件
    	yamlFile, err := ioutil.ReadFile("resources/application.yaml")
    	//若出现错误,打印错误提示
    	if err != nil {
    		fmt.Println(err.Error())
    	}
    	//将读取的字符串转换成结构体conf
    	err = yaml.Unmarshal(yamlFile, c)
    	if err != nil {
    		fmt.Println(err.Error())
    	}
    	return c
    }
    

    后面可以通过debug观察一下,这个返回的c变量,它就类似Java的对象,里边是key-value形式的值——
    image

    最后,就可以根据这些解析到的配置参数,用来驱动连接数据库了。

    创建一个类似旧版mybatis全局的SqlSession变量,就取名为SqlSession即可,该变量起到作用于mybatis的SqlSession实例类似,在数据库驱动连接成功后,即可提供select/insert/update/delete方法。

    var SqlSession *gorm.DB
    

    然后定义一个初始化连接数据库的方法,该方法用于在启动项目时执行——

    func InitMySql()(err error)  {
    	var c conf
    	//获取yaml配置参数
    	conf:=c.getConf()
    	//将yaml配置参数拼接成连接数据库的url
        dsn := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=utf8mb4&parseTime=True&loc=Local",
    		conf.UserName,
    		conf.Password,
    		conf.Url,
    		conf.Port,
    		conf.DbName,
    	)
    	//连接数据库
    	SqlSession,err =gorm.Open("mysql",dsn)
    	if err !=nil{
    		panic(err)
    	}
    	//验证数据库连接是否成功,若成功,则无异常
    	return SqlSession.DB().Ping()
    }
    

    最后,还需要提供一个可以关闭数据库连接的方法——

    func Close()  {
       SqlSession.Close()
    }
    

    到这里,我们就完成了Dao层的搭建,该层里的代码主要负责连接数据库,创建一个取名为SqlSession全局的*gorm.DB变量,该变量作用类似SqlSession,提供了操作数据库的方法,最后,整块dao层的mysql.go代码就如下:

    package dao
    
    import (
    	"github.com/jinzhu/gorm"
    	"io/ioutil"
    )
    
    
    import (
      "github.com/jinzhu/gorm"
       _ "github.com/jinzhu/gorm/dialects/mysql"
    	"io/ioutil"
    	"gopkg.in/yaml.v2"
    	"fmt"
    )
    //指定驱动
    const DRIVER = "mysql"
    
    var SqlSession *gorm.DB
    
    //配置参数映射结构体
    type conf struct {
    	Url string `yaml:"url"`
    	UserName string `yaml:"userName"`
    	Password string `yaml:"password"`
    	DbName string `yaml:"dbname"`
    	Port string `yaml:"post"`
    }
    
    
    //获取配置参数数据
    func (c *conf) getConf() *conf {
    	//读取resources/application.yaml文件
    	yamlFile, err := ioutil.ReadFile("resources/application.yaml")
    	//若出现错误,打印错误提示
    	if err != nil {
    		fmt.Println(err.Error())
    	}
    	//将读取的字符串转换成结构体conf
    	err = yaml.Unmarshal(yamlFile, c)
    	if err != nil {
    		fmt.Println(err.Error())
    	}
    	return c
    }
    
    //初始化连接数据库,生成可操作基本增删改查结构的变量
    func InitMySql()(err error)  {
    	var c conf
    	//获取yaml配置参数
    	conf:=c.getConf()
    	//将yaml配置参数拼接成连接数据库的url
        dsn := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=utf8mb4&parseTime=True&loc=Local",
    		conf.UserName,
    		conf.Password,
    		conf.Url,
    		conf.Port,
    		conf.DbName,
    	)
    	//连接数据库
    	SqlSession,err =gorm.Open(DRIVER,dsn)
    	if err !=nil{
    		panic(err)
    	}
    	//验证数据库连接是否成功,若成功,则无异常
    	return SqlSession.DB().Ping()
    }
    //关闭数据库连接
    func Close()  {
    	SqlSession.Close()
    }
    

    三、entity层定义模型。

    Gorm是全特性的ORM框架,即对象关系映射,这样,就需要类似Java那样建立与数据库映射的类,在Go语言当中,我们称之为结构体。

    首先,先创建一张用于验证的数据库表结构——

    CREATE TABLE `sys_user` (
      `id` int(50) unsigned NOT NULL AUTO_INCREMENT,
      `name` varchar(50) NOT NULL COMMENT '用户名',
      `nick_name` varchar(150) DEFAULT NULL COMMENT '昵称',
      `avatar` varchar(150) DEFAULT NULL COMMENT '头像',
      `password` varchar(100) DEFAULT NULL COMMENT '密码',
      `email` varchar(100) DEFAULT NULL COMMENT '邮箱',
      `mobile` varchar(100) DEFAULT NULL COMMENT '手机号',
      `create_time` bigint(50) DEFAULT NULL COMMENT '更新时间',
      `del_status` tinyint(4) DEFAULT '0' COMMENT '是否删除 -1:已删除   0:正常',
      PRIMARY KEY (`id`),
      UNIQUE KEY `name` (`name`)
    ) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8 COMMENT='用户表';
    

    然后创建一个User.go文件,里边定义一个User结构体——

    type User struct {
       Id int `json:"id"`
       Name string `json:"name"`
       NickName string `json:"nickName"`
       Avatar string `json:"avatar"`
       Password string `json:"password"`
       Email string `json:"email"`
       Mobile string `json:"mobile"`
       DelStatus int `json:"delStatus"`
       CreateTime int64 `json:"createTime"`
    }
    

    注意一点,这里需要明确指出,其struct结构体映射到哪一张表,如果没有显示指出,它会默认生成一张命名为users的数据库表——

    // 数据库表明自定义,默认为model的复数形式,比如这里默认为 users
    func (User) TableName() string {
       return "sys_user"
    }
    

    到这一步,我们就完成了user模型关系建立。

    package entity
    
    // 数据库表明自定义,默认为model的复数形式,比如这里默认为 users
    func (User) TableName() string {
       return "sys_user"
    }
    
    type User struct {
       Id int `json:"id"`
       Name string `json:"name"` 
       NickName string `json:"nickName"`
       Avatar string `json:"avatar"`
       Password string `json:"password"`
       Email string `json:"email"`
       Mobile string `json:"mobile"`
       DelStatus int `json:"delStatus"`
       CreateTime int64 `json:"createTime"`
    }
    

    四、service层建立增删改查业务逻辑。

    在service层建立一个User的service类,命名为UserService.go。

    这里,需要引入两个依赖,一个是dao层创建的全局SqlSession,用于操作数据库;一个是User类,用于接收数据库对应表结构的数据。

    import (
       "go-admin/go/dao"
       "go-admin/go/entity"
    )
    

    接下来,就可以基于SqlSession获取到的API接口,对数据库进行简单的增删改查操作了。

    1.添加User信息

    /**
    新建User信息
     */
    func CreateUser(user *entity.User)(err error)  {
       if err = dao.SqlSession.Create(user).Error;err!=nil{
          return err
       }
       return
    }
    

    2.查询所有的User记录

    /**
    获取user集合
     */
    func GetAllUser()(userList []*entity.User,err error)  {
       if err:=dao.SqlSession.Find(&userList).Error;err!=nil{
          return nil,err
       }
       return
    }
    

    3.根据id删除对应的User信息

    /**
    根据id删除user
     */
    func DeleteUserById(id string)(err error){
       err = dao.SqlSession.Where("id=?",id).Delete(&entity.User{}).Error
       return
    }
    

    4.根据id查询用户User

    /**
    根据id查询用户User
     */
    func GetUserById(id string)(user *entity.User,err error)  {
       if err = dao.SqlSession.Where("id=?",id).First(user).Error;err!=nil{
          return nil,err
       }
       return
    }
    

    5.更新用户信息

    /**
    更新用户信息
     */
    func UpdateUser(user * entity.User)(err error)  {
       err = dao.SqlSession.Save(user).Error
       return
    }
    

    UserService.go的完整代码如下:

    package service
    
    import (
       "go-admin/go/dao"
       "go-admin/go/entity"
    )
    
    
    
    /**
    新建User信息
     */
    func CreateUser(user *entity.User)(err error)  {
       if err = dao.SqlSession.Create(user).Error;err!=nil{
          return err
       }
       return
    }
    
    /**
    获取user集合
     */
    func GetAllUser()(userList []*entity.User,err error)  {
       if err:=dao.SqlSession.Find(&userList).Error;err!=nil{
          return nil,err
       }
       return
    }
    
    /**
    根据id删除user
     */
    func DeleteUserById(id string)(err error){
       err = dao.SqlSession.Where("id=?",id).Delete(&entity.User{}).Error
       return
    }
    
    /**
    根据id查询用户User
     */
    func GetUserById(id string)(user *entity.User,err error)  {
       if err = dao.SqlSession.Where("id=?",id).First(user).Error;err!=nil{
          return nil,err
       }
       return
    }
    
    /**
    更新用户信息
     */
    func UpdateUser(user * entity.User)(err error)  {
       err = dao.SqlSession.Save(user).Error
       return
    }
    

    五、controller层建立User的controller类。

    在controller层建立一个UserController.go类,类似Java的controller,主要用于根据url跳转执行到对应路径的方法。

    首先,引入需要用到的依赖包,

    import (
        //需要用到的结构体
       "go-admin/go/entity"
        //gin框架的依赖
       "github.com/gin-gonic/gin"
        //http连接包
       "net/http"
        //service层方法
       "go-admin/go/service"
    )
    

    接下来,可以实现增删改查的controller方法了。

    1.实现新增User的方法

    func CreateUser(c *gin.Context)  {
       //定义一个User变量
       var user entity.User
       //将调用后端的request请求中的body数据根据json格式解析到User结构变量中
       c.BindJSON(&user)
       //将被转换的user变量传给service层的CreateUser方法,进行User的新建
       err:=service.CreateUser(&user)
       //判断是否异常,无异常则返回包含200和更新数据的信息
       if err!=nil{
          c.JSON(http.StatusBadRequest,gin.H{"error":err.Error()})
       }else {
          c.JSON(http.StatusOK,gin.H{
             "code":200,
             "msg":"success",
             "data":user,
          })
       }
    }
    

    2.查询User的方法

    func GetUserList(c *gin.Context)  {
       todoList,err :=service.GetAllUser()
       if err!=nil{
          c.JSON(http.StatusBadRequest,gin.H{"error":err.Error()})
       }else {
          c.JSON(http.StatusOK,gin.H{
             "code":200,
             "msg":"success",
             "data":todoList,
          })
       }
    }
    

    六、routes层增加路由文件,用于根据请求url进行转发。

    routes层新建一个Routers.go文件。

    首先,同样需要引入需要用到的依赖——

    import (
    "go-admin/go/controller"
    "github.com/gin-gonic/gin"
    )
    

    然后搭建一个初始化路由的操作,将路由统一存放到数据user的group组里,这样可以比较方便区分这些路由数据哪个controller类的。

    Routers.go文件完整代码如下:

    package routes
    
    import (
    "go-admin/go/controller"
    "github.com/gin-gonic/gin"
    )
    
    func SetRouter() *gin.Engine  {
       r :=gin.Default()
    
       /**
       用户User路由组
        */
       userGroup :=r.Group("user")
       {
          //增加用户User
          userGroup.POST("/users",controller.CreateUser)
          //查看所有的User
          userGroup.GET("/users",controller.GetUserList)
          //修改某个User
          userGroup.PUT("/users/:id",controller.UpdateUser)
          //删除某个User
          userGroup.DELETE("/users/:id",controller.DeleteUserById)
       }
       
       return r
    }
    

    七、main启动类。

    最后一步,就是建立main的启动类了,需要注意一点是,go的启动类,必须命名在package main的包下,否则无法进行启动。

    首先,还是需要先引入依赖,main启动类需要用到dao、entity、routers以及mysql驱动包。

    import (
       "go-admin/go/dao"
       "go-admin/go/entity"
       "go-admin/go/routes"
       _ "github.com/jinzhu/gorm/dialects/mysql"
    )
    

    启动方法里的代码主要如下,即前边搭建的东西,这里都有用到了——

    func main()  {
       //连接数据库
       err :=dao.InitMySql()
       if err !=nil{
          panic(err)
       }
       //程序退出关闭数据库连接
       defer dao.Close()
       //绑定模型
       dao.SqlSession.AutoMigrate(&entity.User{})
       //注册路由
       r :=routes.SetRouter()
       //启动端口为8085的项目
       r.Run(":8081")
    }
    

    到这一步,就可以启动项目了,正常情况下,启动成功会显示以下日志信息——
    image

    到这一步,基于Gin+Gorm框架搭建MVC模式的Go后端系统,就初步搭建完成了。

    最后,代码已经上传到GitHub:https://github.com/z924931408/go-admin.git

    欢迎关注公众号,关于思考,关于文化,关于成长——

    image

    作者:朱季谦
    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须在文章页面给出原文链接,否则保留追究法律责任的权利。
  • 相关阅读:
    Beta版本冲刺第二天 12.6
    Beta版本冲刺第一天 12.5
    Linux学习(2)—— 图形化界面
    Linux学习(1)—— 虚拟机安装Linux系统
    IntelliJ IDEA使用
    spring+springmvc+hibernate 整合
    新的篇章
    软件工程实践总结作业——个人作业
    Beta版本冲刺———第七天
    Beta版本冲刺———第六天
  • 原文地址:https://www.cnblogs.com/zhujiqian/p/15074010.html
Copyright © 2020-2023  润新知