前置
Gin是Go语言的一套WEB框架,在学习一种陌生语言的陌生框架,最好的方式,就是用我们熟悉的思维去学。作为一名后端Java开发,在最初入门时,最熟悉的莫过于MVC分层结构,可以简单归纳成controller层,model层,dao层,而在SpringBoot框架里,大概也经常看到以下的分层结构------
这个包含着java根目录和resours资源目录,那么同样go语言我们也可以照这样的架构做一套前后端分离系统。
接下来,我们就按照这个MVC分层结构,搭建一套基于Gin+Gorm框架的Go语言后端。
搭建之前,先简单介绍一下Gin和Gorm分别是什么。
Gin是一个golang的WEB框架,很轻量,依赖到很少,有些类似Java的SpringMVC,通过路由设置,可以将请求转发到对应的处理器上。
Gorm是Go语言的ORM框架,提供一套对数据库进行增删改查的接口,使用它,就可以类似Java使用Hibernate框架一样,可对数据库进行相应操作。
首先,我们要用到这两个框架,得先需要import依赖进来,依赖前。需要go命令安装Gin和Gorm
js
go get -u github.com/gin-gonic/gin
go get -u github.com/jinzhu/gorm
下面,我们可以参考springboot的分层架构,搭建一套MVC分层结构系统:
先新建两个目录go和resource,在go下面新建controller,service,dao,entity层,这里注意的是go语言中还需要一个router包,用于存放路由文件,可能你对路由文件不是很理解,那么,你可以简单理解为,这个路由的作用,就类似SpringMVC的@RequestMapping("/user")和@GetMapping("/list")注解组合起到的作用,通过路由,就可以找到需要调用的后端方法。
然后,在resources目录下新建配置文件,文件名随意。
js
url: 127.0.0.1
userName: root
password: root
dbname: example
post: 3306
基础工作做好,下面就开始敲代码了。
dao层搭建
在dao层下,建立一个mysql.go文件,这个文件在dao的包下,最初的效果如下
这里注意,新建的文件是普通文件并非application文件。
js
package dao
import (
"fmt"
"github.com/jinzhu/gorm"
_ "github.com/jinzhu/gorm/dialects/mysql"
"gopkg.in/yaml.v2"
"io/ioutil"
)
// DRIVER 指定驱动
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()
}
- Goland会自动导包,当然,你自己导包更好了
- 创建一个类似旧版mybatis全局的SqlSession变量,就取名为SqlSession即可,该变量起到作用于mybatis的SqlSession实例类似,在数据库驱动连接成功后,即可提供select/insert/update/delete方法。
- 定义一个用于接收yaml配置参数的struct结构体,你可以简单将它理解为Java的类。
- 然后提供一个读取解析该yaml配置的方法,将读取到的配置参数数据转换成上边的结构体conf
- 定义一个初始化连接数据库的方法,该方法用于在启动项目时执行。
- 最后,还需要提供一个可以关闭数据库连接的方法。
entity层定义模型
Gorm是全特性的ORM框架,即对象关系映射,这样,就需要类似Java那样建立与数据库映射的类,在Go语言当中,我们称之为结构体。
js
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"`
}
创建一个User.go文件,里边定义一个User结构体
js
// TableName 数据库表明自定义,默认为model的复数形式,比如这里默认为 users
func (User) TableName() string {
return "sys_user"
}
注意一点,这里需要明确指出,其struct结构体映射到哪一张表,如果没有显示指出,它会默认生成一张命名为users的数据库表。
service层建立增删改查逻辑
接下来,就可以基于SqlSession获取到的API接口,对数据库进行简单的增删改查操作了。
js
/*
*
新建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
}
- CreateUser :该函数用于
User
在数据库中创建一条新记录。它采用指向实体的指针作为参数,并尝试使用(可能是数据库会话对象)的方法User
将其插入数据库。如果操作成功,则返回(无错误)。否则,它会返回一个错误,指出失败的原因。Create``dao.SqlSession``nil
- GetAllUser
User
:此函数从数据库中检索所有记录的列表。User
它返回一个指向实体 ( )的指针切片userList
和一个错误。它使用Find
的方法从数据库中dao.SqlSession
获取所有记录并将它们存储在切片中。如果没有错误,则返回用户列表;否则,它返回一个错误。User``userList
- DeleteUserById :该函数用于
User
根据提供的信息从数据库中删除一条记录id
。它以id
为字符串参数,并使用Delete
的方法dao.SqlSession
来执行删除操作。它使用方法指定删除的条件Where
,相当于SQLWHERE
子句。如果操作成功,则返回nil
;否则,它返回一个错误。 - GetUserById
User
:此函数根据提供的信息从数据库中检索单个记录id
。它将作为id
字符串参数,并使用First
方法来dao.SqlSession
查找与该Where
方法指定的条件匹配的第一条记录(基于提供的id
)。它将检索到的记录存储在user
指针中。如果操作成功,则返回user
实体;否则,它返回一个错误。 - UpdateUser :此函数用于更新
User
数据库中的现有记录。它以指向实体的指针作为User
参数,并使用 的Save
方法dao.SqlSession
来更新数据库中相应的记录。该Save
方法根据主键自动执行插入(如果记录不存在)或更新(如果记录存在)。如果操作成功,则返回nil
;否则,它返回一个错误。
controller层建立User的controller类。
在controller层建立一个UserController.go类,类似Java的controller,主要用于根据url跳转执行到对应路径的方法。
js
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,
})
}
}
// GetUserList chax
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,
})
}
}
// DeleteUserHandler handles the DELETE request to delete a user by ID.
func DeleteUserById(c *gin.Context) {
id := c.Param("id")
err := service.DeleteUserById(id)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to delete user"})
return
}
c.JSON(http.StatusOK, gin.H{
"code": 200,
"msg": "success",
"data": nil,
})
}
// GetUserHandler handles the GET request to retrieve a user by ID.
func GetUserById(c *gin.Context) {
id := c.Param("id")
user, err := service.GetUserById(id)
if err != nil {
c.JSON(http.StatusNotFound, gin.H{
"code": 200,
"msg": "success",
"data": user,
})
return
}
c.JSON(http.StatusOK, user)
}
// UpdateUserHandler handles the PUT request to update user information.
func UpdateUser(c *gin.Context) {
var user entity.User
if err := c.BindJSON(&user); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request payload"})
return
}
err := service.UpdateUser(&user)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to update user"})
return
}
c.JSON(http.StatusOK, gin.H{
"code": 200,
"msg": "success",
"data": user,
})
}
- CreateUser :此函数是创建新用户的 HTTP POST 请求处理程序。它首先使用 ,从 JSON 请求正文中提取用户数据
c.BindJSON(&user)
,其中user
是类型为 的变量entity.User
。然后,它将用户数据传递给函数service.CreateUser
以在数据库中创建新用户。如果用户创建成功,则会返回状态码200的JSON响应以及创建的用户数据。如果在用户创建过程中出现错误,它会返回带有状态代码 400(错误请求)和错误消息的 JSON 响应。 - GetUserList :此函数是一个 HTTP GET 请求处理程序,用于检索所有用户的列表。它调用该
service.GetAllUser
函数从数据库中获取所有用户。如果检索成功,它将返回带有状态代码 200 的 JSON 响应和用户列表。如果检索过程中出现错误,它会返回带有状态代码 400 的 JSON 响应和错误消息。 - DeleteUserById :此函数是一个 HTTP DELETE 请求处理程序,可按 ID 删除用户。它使用 .url 从 URL 路径中提取用户 ID
c.Param("id")
。然后它调用该service.DeleteUserById
函数从数据库中删除用户。如果删除成功,它将返回带有状态代码 200 的 JSON 响应和成功消息。如果删除过程中出现错误,它会返回带有状态代码 500(内部服务器错误)和错误消息的 JSON 响应。 - GetUserById :此函数是一个 HTTP GET 请求处理程序,可按 ID 检索用户。它使用 .url 从 URL 路径中提取用户 ID
c.Param("id")
。然后,它调用该service.GetUserById
函数根据提供的 ID 从数据库中获取用户。如果找到用户,它会返回带有状态代码 200 和用户数据的 JSON 响应。如果未找到用户,它将返回带有状态代码 404(未找到)的 JSON 响应和成功消息。 - UpdateUser :此函数是更新用户信息的 HTTP PUT 请求处理程序。它使用 .json 从 JSON 请求正文中提取更新的用户数据
c.BindJSON(&user)
。然后它调用该service.UpdateUser
函数来更新数据库中的用户数据。如果更新成功,它将返回带有状态代码 200 的 JSON 响应和更新后的用户数据。如果更新过程中出现错误,它会返回带有状态代码 500 的 JSON 响应和错误消息。
总之,这些函数共同创建了一个基本的用户管理 API,允许创建、检索、更新和删除用户。API 使用 Gin 进行路由和请求处理,并将业务逻辑委托给包service
,包与数据库交互以对用户数据执行 CRUD 操作。
routes层增加路由文件,用于根据请求url进行转发。
js
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)
userGroup.GET("/users/:id", controller.GetUserById)
}
return r
}
-
SetRouter
功能 :该函数负责创建和配置 Gin 路由器 (gin.Engine
) 以及各种与用户相关的路由。 -
r := gin.Default()
:这一行创建了一个新的 Gin 路由器,其中包含一些默认的中间件,包括日志记录和恢复。返回的路由器r
将用于定义和处理 HTTP 路由。 -
userGroup := r.Group("user")
:此行为与用户相关的端点创建路由组。所有以"/user"开头的路由都将分组在此前缀下,使代码更有组织性和可管理性。 -
用户路线:
A。
userGroup.POST("/users", controller.CreateUser)
:此路由处理 HTTP POST 请求以创建新用户。当客户端发送controller.CreateUser
函数,负责根据请求正文中的数据创建新用户。b.
userGroup.GET("/users",controller.GetUser
userGroup.GET("/users", controller.GetUserList):此路由处理 HTTP GET 请求以获取所有用户的列表。当客户端向"/user/users"发送 GET 请求时
controller.GetUserList`函数,它检索用户列表并将其作为 JSON 响应返回。C。
userGroup.PUT("/users/:id", controller.UpdateUser)
:此路由处理 HTTP PUT 请求以通过 ID 更新特定用户。当客户端向"/user"发送 PUT 请求时controller.UpdateUser
函数,它根据提供的 ID 和请求正文更新用户数据。d.
userGroup.DELETE("/users/:id", controller.DeleteUserById)
:此路由处理 HTTP DELETE 请求以删除controller.DeleteUserById
函数,根据提供的 ID 删除用户。e.
userGroup.GET("/users/:id",controller.GetUser
userGroup.GET("/users/:id", controller.GetUserById):此路由处理 HTTP GET 请求以通过 ID 获取特定用户。当客户端向"/user/users/:id"发送 GET 请求时,它将被定向到该
controller.GetUserById`函数,该函数根据提供的 ID 检索用户并将其作为 JSON 响应返回。 -
Return :最后,该函数返回配置好的 Gin 路由器
r
,它可用于启动 Web 服务器并处理传入的 HTTP 请求。
此设置允许服务器通过向"/user"前缀下的指定路由发送适当的 HTTP 请求来处理与用户相关的操作,例如创建、检索、更新和删除用户。每个路由对应于一个特定的控制器功能,负责处理业务逻辑并与数据存储(例如数据库)交互以执行所需的操作。
main启动类以及运行。
最后一步,就是建立main的启动类了,需要注意一点是,go的启动类,必须命名在package main的包下,否则无法进行启动。
js
func main() {
//连接数据库
err := dao.InitMySql()
if err != nil {
panic(err)
}
//程序退出关闭数据库连接
defer dao.Close()
//绑定模型
dao.SqlSession.AutoMigrate(&entity.User{})
//注册路由
r := routes.SetRouter()
//启动端口为8089的项目
r.Run(":8089")
}
下面运行一下
如果是这样就成功了。
到这一步,基于Gin+Gorm框架搭建MVC模式的Go后端系统,就初步搭建完成了。
感悟
gin+Gorm的开发和springboot相似,go的依赖需要通过命令下载,并且导入包的时候没用到的不会保留,这点有点别扭,go的路由也是一个特色,相当于java里面注解,但是这样我觉得反而更清晰明了,同时,后来对controller层进行了优化,让代码的复用性和健壮性更强,还有就是go告诉你的错误方式和java也不一样,熟练以后也挺好用的。这个增删改查让我对gin和gorm有了更加清晰的认识.