gorm是go语言的一个orm框架,用于与数据库交互,完成CRUD操作。gin-vue-admin项目,也是使用gorm框架来操作数据库的。在项目中,涉及到数据的增删查改的业务,内部实现原理大部分情况下都是后端与数据库交互所完成的。因此,可以从前端来找到有关数据的增删查改场景来学习gorm框架的使用。

1 查询操作

在前端 超级管理员 菜单下的子菜单项,均为和数据库操作相关的场景

我们可以用api管理来作为样例学习gorm框架。

在点击前端的 api管理 后,前端就会展示api的列表。因此可以查看前端是请求了后端哪个接口,以此来找到后端最终查询数据库的操作。

请求体如下,用于分页请求。

{"page":1,"pageSize":10}

注:虽然前端写的接口名字是/api/api/getApiList,但这api在前端经过了跨域处理,会将请求打到后端的/api/getApiList接口上,具体跨域如下图代码(前端代码根目录/vite.config.js):

因此,可以看出请求了后端的/api/getApiList接口。接下来就需要看这个接口实际做了什么操作,来获取到的api列表。

具体的后端代码如下

func (s *SystemApiApi) GetApiList(c *gin.Context) {var pageInfo systemReq.SearchApiParams_ = c.ShouldBindJSON(&pageInfo)if err := utils.Verify(pageInfo.PageInfo, utils.PageInfoVerify); err != nil {response.FailWithMessage(err.Error(), c)return}if list, total, err := apiService.GetAPIInfoList(pageInfo.SysApi, pageInfo.PageInfo, pageInfo.OrderKey, pageInfo.Desc); err != nil {global.GVA_LOG.Error("获取失败!", zap.Error(err))response.FailWithMessage("获取失败", c)} else {response.OkWithDetailed(response.PageResult{List:     list,Total:    total,Page:     pageInfo.Page,PageSize: pageInfo.PageSize,}, "获取成功", c)}
}

调用apiService.GetAPIInfoList方法.在方法中最开始有一行代码是这样的:

db := global.GVA_DB.Model(&system.SysApi{})

这是将db绑定到system.SysApi所对应的表,该对象代码如下:

type SysApi struct {global.GVA_MODELPath        string `json:"path" gorm:"comment:api路径"`             // api路径Description string `json:"description" gorm:"comment:api中文描述"`    // api中文描述ApiGroup    string `json:"apiGroup" gorm:"comment:api组"`          // api组Method      string `json:"method" gorm:"default:POST;comment:方法"` // 方法:创建POST(默认)|查看GET|更新PUT|删除DELETE
}func (SysApi) TableName() string {return "sys_apis"
}

因此可以得知,上面global.GVA_DB.Model(&system.SysApi{})是将db绑定到sys_apis表上。具体可以点进去Gorm的源代码查看

// Model specify the model you would like to run db operations
//    // update all users's name to `hello`
//    db.Model(&User{}).Update("name", "hello")
//    // if user's primary key is non-blank, will use it as condition, then will only update the user's name to `hello`
//    db.Model(&user).Update("name", "hello")
func (db *DB) Model(value interface{}) (tx *DB) {tx = db.getInstance()tx.Statement.Model = valuereturn
}

查看这个源代码,对于代码是什么意思,在我们当前使用阶段并不重要,重要的是要知道这段代码是要做什么操作,因此直接可以看注释。注释的大概意思就是使用了Modle后,后边你对db的操作都是对你绑定的这个表格的操作。

SysApi中除了自身的api相关参数外,还组合了global.GVA_MODEL结构体

type GVA_MODEL struct {ID        uint           `gorm:"primarykey"` // 主键IDCreatedAt time.Time      // 创建时间UpdatedAt time.Time      // 更新时间DeletedAt gorm.DeletedAt `gorm:"index" json:"-"` // 删除时间
}

包含了主键ID,创建修改删除的时间字段。

其实最重要的就是上述的调用apiService.GetAPIInfoList方法.点进去后是有4个判断

 if api.Path != "" {db = db.Where("path LIKE ?", "%"+api.Path+"%")}if api.Description != "" {db = db.Where("description LIKE ?", "%"+api.Description+"%")}if api.Method != "" {db = db.Where("method = ?", api.Method)}if api.ApiGroup != "" {db = db.Where("api_group = ?", api.ApiGroup)}

从代码来看,主要是增加查找的条件,若Path不空,就相当于在SQL的where语句后增加path LIKE %xxxx%来指定path,下面的几个判断也是同理。

然后来了一句

err = db.Count(&total).Error

这个是判断增加上述条件后,目前结果是否有异常。接着往下看,在没有异常的时候,会执行下面的代码

db = db.Limit(limit).Offset(offset)
if order != "" {var OrderStr string// 设置有效排序key 防止sql注入// 感谢 Tom4t0 提交漏洞信息orderMap := make(map[string]bool, 5)orderMap["id"] = trueorderMap["path"] = trueorderMap["api_group"] = trueorderMap["description"] = trueorderMap["method"] = trueif orderMap[order] {if desc {OrderStr = order + " desc"} else {OrderStr = order}} else { // didn't matched any order key in `orderMap`err = fmt.Errorf("非法的排序字段: %v", order)return apiList, total, err}err = db.Order(OrderStr).Find(&apiList).Error
} else {err = db.Order("api_group").Find(&apiList).Error
}

第1行的db.Limit(limit).Offset(offset)正好就是前端传入的分表字段。

由于go语言没有set类型,因此用string:bool来表示set类型,这从6-17行主要是判断用户是否需要按某个字段来排序。通过.Order(xxx)来指定使用哪个字段排序。

默认使用api_group字段来排序。因此,我们前端展示时候,默认是通过api组来排序的

err = db.Order("api_group").Find(&apiList).Error

然后Find(&apiList)将查询到的结果赋值给此结构体列表。

2 增加操作

对于GORM的增加操作,还是可以继续使用这个api管理的场景,前端上方有个新增的按钮,可以点击这个按钮,新增一个api。然后查看调用了哪个后端的接口。

如填入如下数据:


点确定后会调用对应的新增接口

可以看出,调用了/api/createApi接口,方法为post方法,请求体为:

{"path": "/test/api-test","apiGroup": "test","method": "POST","description": "测试api"
}

接着就就可以去后端找到对应的接口来查看执行逻辑。

func (s *SystemApiApi) CreateApi(c *gin.Context) {var api system.SysApi_ = c.ShouldBindJSON(&api)if err := utils.Verify(api, utils.ApiVerify); err != nil {response.FailWithMessage(err.Error(), c)return}if err := apiService.CreateApi(api); err != nil {global.GVA_LOG.Error("创建失败!", zap.Error(err))response.FailWithMessage("创建失败", c)} else {response.OkWithMessage("创建成功", c)}
}

首先将传入的请求体,反序列化为system.SysApi结构体对象。

对于此接口最主要的还是调用了apiService.CreateApi(api)来创建api。可以看下这个方法的代码

func (apiService *ApiService) CreateApi(api system.SysApi) (err error) {if !errors.Is(global.GVA_DB.Where("path = ? AND method = ?", api.Path, api.Method).First(&system.SysApi{}).Error, gorm.ErrRecordNotFound) {return errors.New("存在相同api")}return global.GVA_DB.Create(&api).Error
}

代码也比较简单。首先通过pathmethon字段来确定该api是否被注册,若没有被注册,则注册该api

通过global.GVA_DB.Create(&api)就可以创建该api。

当写到这里时候,我突然有了个疑问,为什么在上面的查询操作里,使用了global.GVA_DB.Model(&system.SysApi{}),而在新增操作时候并没有使用。后来回过头再看了Model源代码的注释,其实使用Model的话,就类比我们写sql时候使用的where语句,将这些where语句与表对应。而我们新增操作时候,类比于写sql,并不需要where语句,所以就不需要Model方法。

3 修改操作

对于修改操作,从前端可以修改我们刚才新增的api

比如我把api的Post方法改为Get方法,点确定提交后,前端请求了后端的/api/updateApi方法,是POST方法,请求体内容如下

{"ID": 93,"CreatedAt": "2022-07-16T15:43:28.022+08:00","UpdatedAt": "2022-07-16T15:43:28.022+08:00","path": "/test/api-test","description": "测试api","apiGroup": "test","method": "GET"
}

然后找到对应后端接口的方法

func (s *SystemApiApi) UpdateApi(c *gin.Context) {var api system.SysApi_ = c.ShouldBindJSON(&api)if err := utils.Verify(api, utils.ApiVerify); err != nil {response.FailWithMessage(err.Error(), c)return}if err := apiService.UpdateApi(api); err != nil {global.GVA_LOG.Error("修改失败!", zap.Error(err))response.FailWithMessage("修改失败", c)} else {response.OkWithMessage("修改成功", c)}
}

最开始和createApi接口的方法一样,将传入的请求体反序列化为system.SysApi结构体对象。

最主要的还是调用了apiService.UpdateApi(api)此方法

func (apiService *ApiService) UpdateApi(api system.SysApi) (err error) {var oldA system.SysApierr = global.GVA_DB.Where("id = ?", api.ID).First(&oldA).Errorif oldA.Path != api.Path || oldA.Method != api.Method {if !errors.Is(global.GVA_DB.Where("path = ? AND method = ?", api.Path, api.Method).First(&system.SysApi{}).Error, gorm.ErrRecordNotFound) {return errors.New("存在相同api路径")}}if err != nil {return err} else {err = CasbinServiceApp.UpdateCasbinApi(oldA.Path, api.Path, oldA.Method, api.Method)if err != nil {return err} else {err = global.GVA_DB.Save(&api).Error}}return err
}

首先判断是否修改了path和method字段,若修改了就查询数据库中是否已经有了该字段的api。后续先将api更新到casbin_rule表中,此表是之前写中间件学习记录时候,里边casbin用到的库,主要用于判断某个用户是否有权限使用某个api。

接下来是global.GVA_DB.Save(&api)操作,直接将api变量所对应的信息更新到数据库中,gorm官方文档是这样介绍Save的。其实就是根据主键来更新所有字段

4 删除操作

依旧可以用刚才创建的api来进行删除操作,在前端点击删除后,会去请求后端的/api/deleteApi方法,类型为POST,请求体为:

{"ID": 93,"CreatedAt": "2022-07-16T15:43:28.022+08:00","UpdatedAt": "2022-07-16T16:03:01.181+08:00","path": "/test/api-test","description": "测试api","apiGroup": "test","method": "GET"
}

查看后端调用的代码

func (s *SystemApiApi) DeleteApi(c *gin.Context) {var api system.SysApi_ = c.ShouldBindJSON(&api)if err := utils.Verify(api.GVA_MODEL, utils.IdVerify); err != nil {response.FailWithMessage(err.Error(), c)return}if err := apiService.DeleteApi(api); err != nil {global.GVA_LOG.Error("删除失败!", zap.Error(err))response.FailWithMessage("删除失败", c)} else {response.OkWithMessage("删除成功", c)}
}

主要是调用了apiService.DeleteApi(api)方法

func (apiService *ApiService) DeleteApi(api system.SysApi) (err error) {var entity system.SysApierr = global.GVA_DB.Where("id = ?", api.ID).First(&entity).Error // 根据id查询api记录if errors.Is(err, gorm.ErrRecordNotFound) {                      // api记录不存在return err}err = global.GVA_DB.Delete(&entity).Errorif err != nil {return err}CasbinServiceApp.ClearCasbin(1, entity.Path, entity.Method)return nil
}

这里首先通过主键来判断是否有该api,如果有的话,后边就调用global.GVA_DB.Delete(&entity)来删除这条记录。需要补充的是,这里的删除不是真正的删除,而是软删除。

此时我们查看数据库的内容,发现确实并没有删除,而是delete_at字段多了内容

gin-vue-admin学习(后端篇)—— 3.GORM相关推荐

  1. gin+vue的前后端分离开源项目

    该项目是gin+vue的前后端分离项目,使用gorm访问MySQL,其中vue前端是使用vue-element-admin框架简单实现的: go后台使用jwt,对API接口进行权限控制.此外,Web页 ...

  2. Spring Boot Vue Admin 前后端完全分离的权限控制模版

    Spring Boot Vue Admin 简介 提供一套前后端分离的后台权限管理模版,按钮级别的权限控制. 前端 Vue 模板来自 vue-element-admin,其他功能可以根据该项目再进行拓 ...

  3. java springboot VUE 在线学习平台系统开发mysql数据库web结构java编程计算机网页源码maven项目前后端分离

    一.源码特点   springboot VUE 在线学习平台系统是一套完善的完整信息管理类型系统 前后端分离,结合springboot框架和VUE完成本系统,对理解JSP java编程开发语言有帮助系 ...

  4. dotnetcore+vue+elementUI 前后端分离 三(前端篇)

    说明: 本项目使用了 mysql employees数据库,使用了vue + axois + element UI 2.0 ,演示了 单页程序 架构 ,vue router 的使用,axois 使用, ...

  5. vue.js路由配置vue-router的基础学习 - 概念篇

    文章目录 引言 · 相关问题小结: 一.动态路由匹配 (两种情况) A. 两种情况,代码对比: B. 两种情况,效果图对比: C. 提醒 · 仔细体会: D. 优先级的问题: 二.嵌套路由 引言 · ...

  6. VUE源码学习第一篇--前言

    一.目的 前端技术的发展,现在以vue,react,angular为代表的MVVM模式以成为主流,这三个框架大有三分天下之势.react和angular有facebook与谷歌背书,而vue是以一己之 ...

  7. SpringBoot+MyBatisPlus+Vue 前后端分离项目快速搭建【后端篇】【快速生成后端代码、封装结果集、增删改查、模糊查找】【毕设基础框架】

    前后端分离项目快速搭建[后端篇] 数据库准备 后端搭建 1.快速创建个SpringBoot项目 2.引入依赖 3.编写代码快速生成代码 4.运行代码生成器生成代码 5.编写application.pr ...

  8. dotnetcore+vue+elementUI 前后端分离架 二(后端篇)

    前言 最近几年前后端分离架构大行其道,而且各种框架也是层出不穷.本文通过dotnetcore +vue 来介绍 前后端分离架构实战. 涉及的技术栈 服务端技术 mysql 本项目使用mysql 作为持 ...

  9. vue入门学习(基础篇)

    vue入门学习总结: vue的一个组件包括三部分:template.style.script. vue的数据在data中定义使用. 数据渲染指令:v-text.v-html.{{}}. 隐藏未编译的标 ...

  10. Vue的学习(一:基本使用到路由部分)

    官网 https://cn.vuejs.org/ 前言 封装 VS 库 VS 框架 封装通常指一小部分通用业务逻辑,多个封装形成一个模块或者文件,多个模块或者文件就发展成为库或者框架,而插件是为库或者 ...

最新文章

  1. python的5种高级用法
  2. ffmpeg linux 命令,Linux命令行下转换媒体格式工具FFMPEG介绍
  3. 尤雨溪开发的 vue-devtools 如何安装,为何打开文件的功能鲜有人知?
  4. 我,宇宙最强编辑器,支持远程开发
  5. log4net使用指南(转载)
  6. google Chrome 浏览器源码地址地址!
  7. 求二叉树中结点个数代码java_求二叉树中第K层结点的个数
  8. MQTT连接服务器返回2
  9. 关于“指针数组”和”数组指针“
  10. 20160801java学习重点:函数
  11. 38译码器和416译码器
  12. LoadRunner教程(13)-LoadRunner 服务水平协议
  13. “Deep Freeze冰点还原”解冻操作
  14. logistic regression(二项 logistic 与 多项logistic )
  15. DEFCON携手百度安全落地中国,打造国际化网络安全交流平台
  16. Linux下安装网易云音乐
  17. 牛客网习题集 - Wannafly挑战赛13- D applese生日
  18. uniapp 动态切换应用图标、名称插件(如新年、国庆等) Ba-ChangeIcon
  19. VB.NET入门(一)
  20. 2021年全国各地区居民人均可支配收入排行榜:上海、北京人均可支配收入超过7万元,8个省份高于全国平均水平(附年榜TOP31详单)

热门文章

  1. php电子报账系统,我校财务网上自助报账系统上线运行
  2. 网站推广大法20招(各网站适用!)
  3. Error:Cannot fit requested classes in a single dex file.。。。编译错误
  4. 前端下载文件的写法(兼容IE(IE9+) Firefox chrome)
  5. python实现批量下载Excel中PDF的URL到本地
  6. Bundle Adjustment (BA)简述
  7. Struts-国际化支持
  8. 自动驾驶汽车传感器数字孪生建模(二)
  9. 火影T6A 游戏本 评测
  10. PhotoView踩坑