基于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:\Users\Administrator\go下:

image

通过Go命令安装的依赖包放在这个目录底下C:\Users\Administrator\go\src下:

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:GitHub - z924931408/go-admin: 这是个人Go语言基于Gin+gorm框架搭建的MVC结构的后端模块。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/147798.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

程序员的护城河:技术深度与软实力的并重

程序员的护城河:技术深度与软实力的并重 作为一名资深的IT行业从业者,我一直在思考一个问题:在我们这个以数据驱动和技术创新为核心的时代,程序员的价值究竟体现在哪里?毫无疑问,程序员在维护系统安全、数…

使用grad-cam对ViT的输出进行可视化

使用grad-cam对ViT的输出进行可视化 文章目录 使用grad-cam对ViT的输出进行可视化前言原理使用代码Pytorch-grad-cam库的更多方法在MMpretrain中使用示例总结 前言 Vision Transformer (ViT) 作为现在CV中的主流backbone,它可以在图像分类任务上达到与卷积神经网络…

22款奔驰E260L升级原厂360全景影像 高清环绕的视野

360全景影像影像系统提升行车时的便利,不管是新手或是老司机都将是一个不错的配置,无论是在倒车,挪车以及拐弯转角的时候都能及时关注车辆所处的环境状况,避免盲区事故发生,提升行车出入安全性。 360全景影像包含&…

ffmpeg5及以上-s和像素格式转换 画屏问题

环境: lsb_release -a No LSB modules are available. Distributor ID: Ubuntu Description: Ubuntu 22.10 Release: 22.10 Codename: kinetic拉下ffmpeg源码,6.0.1,4.3.6,5.1.4,依次安装作实验 ./configure --disable-x86asm …

香港丽晶酒店于维港畔隆重开幕 绮丽传奇重现香江

2023年11月14日,上海 – 日前,洲际酒店集团旗下香港丽晶酒店于2023年11月8日隆重开幕,历经华丽蜕变后正式回归维多利亚港畔,旧日经典再次伫立,唤醒过去的美好记忆。香港丽晶酒店新生后成功举办了规模盛大的开幕盛典活动…

计算属性传参的写法,在vue3项目中,

计算属性 | Vue.js 在vue3项目中,使用计算属性,在使用这个计算属性时,要传入参数写法有点怪 computed(函数) 函数里面再返回一个函数,这个函数接收参数 注意:最后的结果是,这个计算属性函数并…

【WIFI】MTK WiFi降sar如何开发

1.Sar 简介 SAR即英语“Specific Absorption Rate”的缩写。SAR值一般指手机产品中电磁波所产生的热能,它是对人体产生影响的衡量数据,单位是W/Kg(瓦/公斤)。 对于测量手机产品的“SAR”,通俗地讲,就是测量手机辐射对人体的影响是否符合标准。国际通用的标准为:以6分钟…

confluence无法打开空间目录

confluence无法打开空间目录,打开空间目录后无法显示项目 查看项目的类别信息都在 问题原因 由于索引损坏导致; This issue is caused by acorrupted index. Confluence is trying to fetch information about the spacesfrom the available index, …

如何在Windows 10上恢复丢失的文件?

丢失文件时该怎么办? 在使用电脑的过程中,我们经常会遇到丢失重要文件的情况。无论是意外删除、病毒攻击还是电脑格式化,都可能导致文件丢失。在面对这些情况时,大多数人总是会问:“如何在电脑上恢复丢失的文件&am…

44. Adb调试QT开发的Android程序实用小技巧汇总

1. 说明 使用QT开发Android应用时,如果程序本身出现了问题,很难进行调试。不像在linux或者windows系统中,可以利用QtCreator软件本身进行一些调试,安卓应用一旦在系统中安装后,如果运行中途出现什么BUG,定位问题所在很麻烦。不过,好在有adb这种调试工具可以代替QtCreat…

NineData慢查询分析:数据库性能优化的专家

在日常的数据库运维中,慢查询是一个常见的问题,它可能由复杂的查询语句、不充分的索引设计、大量数据的处理、硬件资源不足等多种因素引起。这些慢查询会消耗大量的数据库服务器资源,甚至可能导致数据库死机,无法响应业务请求。因…

【联邦学习+区块链】TORR: A Lightweight Blockchain for Decentralized Federated Learning

文章目录 I.CONTRIBUTIONII. ASSUMPTIONS AND THREAT MODELA. AssumptionsB. Threat Model III. SYSTEM DESIGNA. Design OverviewB. Block DesignC. InitializationD. Role SelectionE. Storage ProtocolF. Aggregation ProtocolG. Proof of ReliabilityH. Blockchain Consens…

Flink(五)【DataStream 转换算子(上)】

前言 这节注定是一个大的章节,我预估一下得两三天,涉及到的一些东西不懂就重新学,比如 Lambda 表达式,我只知道 Scala 中很方便,但在 Java 中有点发怵了;一个接口能不能 new 来构造对象? 答案是可以的&…

兼容最新 urllib3 版本及相关库

解决方案 对于这个问题,我们可以通过修改setup.py文件来解决。在setup.py文件中,我们将urllib3的版本范围从1.21.1到1.26改为1.21.1到最新版本。这是因为在patch中,我们已经检查了urllib3的版本,并确保其大于1…

编程的简单实例,编程零基础入门教程,中文编程开发语言工具下载

编程的简单实例,编程零基础入门教程,中文编程开发语言工具下载 给大家分享一款中文编程工具,零基础轻松学编程,不需英语基础,编程工具可下载。 这款工具不但可以连接部分硬件,而且可以开发大型的软件&…

深兰科技轮腿家用AI机器人荣获“2023年度城市更新科创大奖”

近日,“2023金砖论坛第五季金立方城市更新科创大会”在上海举行,会上发布了《第12届金砖价值榜》,深兰科技研发出品的轮腿式家用AI机器人(兰宝),因其AI技术的创新性应用,荣获了“2023年度城市更新科创大奖”。 在10月2…

JavaScript数据存储

原始类型:存储在栈内存中,每次开辟的空间大小是固定 引用类型(对象、函数、数组):存储在堆内存中,开辟的空间大小根据数据的大小决定 // 声明变量会在栈内存中开辟空间 // 创建对象在堆内存中开辟空间&…

使用 Cloudflare Worker 免费搭建网址导航网站

开源项目 GitHub:https://github.com/sleepwood/CF-Worker-Dir/ CloudFlare Worker:https://workers.cloudflare.com/ 搭建教程 首先,进入cloud flare - Worker 截图20200224180010.png 在 Cloudflare Worker 管理页面创建一个新的 Work…

11-15 AOP配置

AOP配置 基于xml 切入点表达式:方法签名描述 方法签名:访问修饰符返回值类型〔包.类.]方法名(参数列名)throws 异常声明; 语法: execution(修饰符?返回值 方法名(参数) 异常?) 注意: ?:0或者1个 通配符: * : 任意 用于返回值,方法名,类名 .. : 任意包中使用: ..:表示该包,…

Visual Studio 2019 C# 断点调试代码内存窗口显示无法计算表达式的解决问题

查看如下界面,发现右下角内存1窗口显示无法计算表达式: 按照如下步骤操作即可: 如果s1局部变量此时有值,但是内存窗口还是无法计算表达式我们可以