文/朱季谦

环境准备:安装Gin与Gorm

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

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

这个结构分为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下:

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

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

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

先创建一个Go项目,这里,我取名为go-admin,底下创建一个go目录,用于存放Go代码;一个resources资源目录,存放配置文件,结构如下——



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

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

接下来,是在resources资源目录创建一个application.yaml配置文件,当然,这个配置文件可以随便命名,不用像SpringBoot那样需要考虑其命名背后所代表的优先级。



这个配置文件里,就存放需要用到的Mysql数据库连接信息:

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

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

二、dao层的搭建。

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

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

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

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

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

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

除此之外,还提供了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"
)

到这一步,效果如下:

这里爆红色是正常的,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形式的值——

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

创建一个类似旧版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")
}

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

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

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

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

基于Gin+Gorm框架搭建MVC模式的Go语言后端系统的更多相关文章

  1. 基于gin web框架搭建RESTful API服务

    这篇主要学习go项目中的项目结构.项目规范等知识,ROM采用的database/sql的写法. 1.技术框架 利用的是ginweb框架,然后ROM层选用database/sql,安装mysql驱动.安 ...

  2. 基于jsp+servlet+javabean的MVC模式简单应用

    原先写在CSDN的一篇,我直接扒过来吧.之前打算在CSDN的,结果写了几回,发现他那个发布系统简直烂到家,经常丢失图片各种.所以很长一段时间我也没写什么. 一.MVC模式 1.M :  javabea ...

  3. 基于.NET Core 框架搭建WebApi项目

    一 什么是.NET Core? 随着2014年 Xamarin和微软发起.NET基金会,微软在2014年11月份开放.NET框架源代码.在.NET开源基金会的统一规划下诞生了.NET Core .也就 ...

  4. tp框架的MVC模式

    一.定义 MVC全名是Model View Controller,是模型(model)-视图(view)-控制器(controller)的缩写,一种软件设计典范,用一种业务逻辑.数据.界面显示分离的方 ...

  5. Dubbo——基于Zookeeper服务框架搭建及案例演示

    一.了解SOA微服务架构 在大规模服务化之前,应用可能只是通过RMI或Hessian等工具,简单的暴露和引用远程服务,通过配置服务的URL地址进行调用,通过F5等硬件进行负载均衡. (1) 当服务越来 ...

  6. 用实体框架搭建MVC程序框架(全部)

    第一步:1.新建项目 2.新建domain类库 3.新建Data类库 4.为上面的1.2.3添加实体框架nuget包.(可以右键管理nuget包来查找entityframework,当然也可以通过程序 ...

  7. 二十七、EFW框架BS系统开发中的MVC模式探讨

    回<[开源]EFW框架系列文章索引>        EFW框架源代码下载V1.3:http://pan.baidu.com/s/1c0dADO0 EFW框架实例源代码下载:http://p ...

  8. Spring mvc 模式小结

    http://www.taobaotesting.com/blogs/2375 1.spring mvc简介 Spring MVC框架是一个MVC框架,通过实现Model-View-Controlle ...

  9. struts2的MVC模式

    MVC是一种架构型模式,它本身并不引入新的功能,只是用来指导我们改善应用程序的架构,使得应用的模型和视图相分离,从而得到更好的开发和维护效率. 在MVC模式中,应用程序被划分成了模型(Model).视 ...

随机推荐

  1. 四、SSL虚拟证书

    沿用练习三,配置基于加密网站的虚拟主机,实现以下目标: 域名为www.c.com 该站点通过https访问 通过私钥.证书对该站点所有数据加密 4.2 方案 源码安装Nginx时必须使用--with- ...

  2. 【NX二次开发】Block UI对话框-代码生成部分

    常规: 语言:生成的代码语言 生成附注:是否生成注释代码 生成特定与块的代码: 输入点: 回调:对话框通过回调函数调用,例如通过另一个对话框的按钮调用本对话框 菜单:对话框通过菜单调用 用户出口:对话 ...

  3. Netty 面试题 (史上最全、持续更新)

    文章很长,建议收藏起来,慢慢读! 疯狂创客圈为小伙伴奉上以下珍贵的学习资源: 疯狂创客圈 经典图书 : <Netty Zookeeper Redis 高并发实战> 面试必备 + 大厂必备 ...

  4. 「模拟8.21」山洞(矩阵优化DP)

    暴力: 正解: 考虑循环矩阵,f[i][j]表示从i点到j点的方案数 我们发现n很小,我们预处理出n次的f[i][j] 然后在矩阵快速幂中,我们要从当前的f[i][j]*f[j][k]-->fi ...

  5. NOIP模拟测试21「折纸&#183;不等式」

    折纸 题解 考试时无限接近正解,然而最终也只是接近而已了 考虑模拟会爆炸,拿手折纸条试一试,很简单 考你动手能力 代码 #include<bits/stdc++.h> using name ...

  6. 剖析虚幻渲染体系(06)- UE5特辑Part 1(特性和Nanite)

    目录 6.1 本篇概述 6.1.1 本篇内容 6.1.2 基础概念 6.2 UE5新特性 6.2.1 UE5编辑器 6.2.1.1 下载编辑器及资源 6.2.1.2 启动示例工程 6.2.1.3 编辑 ...

  7. 生产环境部署Django项目

    生产环境部署Django项目 1.  部署架构 IP地址 安装服务 172.16.1.251 nginx uwsgi(sock方式) docker mysql5.7 redis5 Nginx 前端We ...

  8. 信奥赛一本通1573:分离与合体C++分离与合体

    题目链接 #include<cstdio> #include<algorithm> using namespace std; int dp[305][305]={},jojo[ ...

  9. VSCode 使用 Code Runner 插件无法编译运行文件名带空格的文件

    本文同时在我的博客发布:VSCode 使用 Code Runner 插件无法编译运行文件名带空格的文件 - Skykguj 's Blog (sky390.cn) 使用 Visual Studio C ...

  10. Netty Recycler的源码分析

    Recycler分析 调用来源:PooledByteBuf.java 涉及的知识: AtomicInteger WeakReference ThreadLocal 在DefaultHandle 中调用 ...