You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

195 lines
8.0 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

### 本篇将介绍我们集成的 gorm v2 操作非常流畅的增删改查功能
> 1.gormv2 功能非常强大,本篇将介绍 gorm_v2 在 GinSkeleton 中非常简洁、简单的操作流程,以 增删改查 操作为例介绍.
> 2.阅读完本篇,您可以继续阅读官方文档,学习更多功能:https://gorm.io/zh_CN/docs/
### 前言
> 1.一个简单的 CURD 操作,我们的起始点为表单参数验证器,终点为数据写入数据库,接下来流程我们将沿着这个主线展开编写.
### 用户表单参数验证器
```code
// 给表单参数验证器设置 form 标签gin框架会获取用户提交的表单参数绑定在此结构体上
// 设置 json 标签 GinSkeleton 会将json对应的字段绑定在上下文(gin.Context)
type UserStore struct {
Base // Base 表示你可以继续组合其他结构体
Pass string `form:"pass" json:"pass" binding:"required,min=6"`
RealName string `form:"real_name" json:"real_name" binding:"required,min=2"`
Phone string `form:"phone" json:"phone" binding:"required,len=11"`
Remark string `form:"remark" json:"remark" `
}
// 验证器语法,更详细用法参见常用开发模块列表专项介绍
func (u UserStore) CheckParams(context *gin.Context) {
// 省略代码...
}
```
### 验证器完成进入控制器,控制器可以直接将 gin.Context 继续传递给 UsersModel
> 1.以下代码将以 model目录 > users 模型展开
```code
// 创建 userFactory
// 参数说明: 传递空值,默认使用 配置文件选项UseDbTypemysql
// 以下函数为固定写法,复制即可,不需要深度研究
func CreateUserFactory(sqlType string) *UsersModel {
return &UsersModel{BaseModel: model.BaseModel{DB: model.UseDbConn(sqlType)}}
}
type UsersModel struct {
model.BaseModel // BaseModel 主要有Id 、 CreatedAt 、UpdatedAt 字段这里主要是演示UsersModel支持结构体的组合
UserName string `gorm:"column:user_name" json:"user_name"`
Pass string `json:"pass"`
Phone string `json:"phone"`
RealName string `gorm:"column:real_name" json:"real_name"`
Status int `json:"status"`
Remark string `json:"remark"`
LastLoginIp string `gorm:"column:last_login_ip" json:"last_login_ip"`
}
// 设置表名
func (u *UsersModel) TableName() string {
return "tb_users"
}
// UsersModel 结构体组合了 *gorm.DB 的所有功能,您可以通过 u.xxx 直接调用 gorm.DB 的所有功能
```
#### 1.新增数据
> 以下代码引用了 `data_bind.ShouldBindFormDataToModel(c, &tmp)` 函数,这个函数是我们对 gin.ShouldBind 函数的精简,加快数据绑定效率。
> 1.1 参数绑定的原则model 定义的结构体字段和表单参数验证器结构体设置的json标签名称、数据类型一致才可以绑定, UserModel 支持类似BaseModel等结构体组合.
> 1.2 gorm 的数据新增函数 Create 支持单条、批量,如果是批量,只需要定义被添加的数据为 切片即可,例如 var tmp []UsersModel , u.Create(&tmp)
```code
//新增数据
func (u *UsersModel) InsertData(c *gin.Context) bool {
// 注意: 必须重新定义一个 userModel 变量
var tmp UsersModel
// data_bind.ShouldBindFormDataToModel 函数主要按照 UsersModel 结构体指定的json标签去gin.Context上去寻找相同名称的表单数据,绑定到新定义的变量.
// 这里不能使用 gin.ShouldBind 函数从上下文绑定数据,因为 UserModel 我们组合了 gorm.DB 该函数功能太强大会深入内部持续解析gorm.Db产生死循环
// 使用我们提供的简化版本函数data_bind.ShouldBindFormDataToModel代替 gin.ShouldBind 即可
if err := data_bind.ShouldBindFormDataToModel(c, &tmp); err == nil {
// Create 函数会将新插入的数据Id 继续更新到 tmp 结构体的主键ID 字段,这里必须传递 指针. 最终的 tmp 其实就是一条新增加的完整数据
// 注意: 在本项目骨架 Create 参数必须传递 指针类型这样才能支持gorm的回调函数
if res := u.Create(&tmp); res.Error == nil {
return true
} else {
variable.ZapLog.Error("UsersModel 数据新增出错", zap.Error(res.Error))
}
}else {
variable.ZapLog.Error("UsersModel 数据绑定出错", zap.Error(err))
}
return false
}
```
> 1.3 关于model变量从上下文如何绑定附一张绑定数据的逻辑图也可以帮助大家理解.
![数据绑定原理](https://www.ginskeleton.com/images/bind_explain.png)
#### 2.修改数据
> 2.1 gorm 的数据更新有两个函数: updates 不会处理零值字段save 会全量覆盖式更新字段
> 2.2 u.Updates() 函数会根据 UsersModel 已经绑定的 TableName 函数解析对应的数据表,然后根据 tmp 结构体定义的主键Id去更新其他字段值.
> 2.3 更新时可以搭配 gorm_v2 提供的 Select() 指定字段更新例如gorm.Db.Select(字段1,字段2,字段3..) ,也可以设置忽略特定字段更新数据,例如: gorm.Db.Omit(字段1,字段2,字段3..)
```code
//更新
func (u *UsersModel) UpdateData(c *gin.Context) bool {
var tmp UsersModel
if err := data_bind.ShouldBindFormDataToModel(c, &tmp); err == nil {
//tmp 会被自动绑定 CreatedAt、UpdatedAt 字段,更新时我们不希望更新 CreatedAt 字段,使用 Omit 语法忽略该字段
// 注意: 在本项目骨架 Save、Update 参数必须传递 指针类型这样才能支持gorm的回调函数
if res := u.Omit("CreatedAt").Save(&tmp); res.Error == nil {
return true
} else {
variable.ZapLog.Error("UsersModel 数据更更新出错", zap.Error(err))
}
}
return false
}
```
#### 3.单条删除数据
> UsersModel 已经绑定了函数 TableName ,所以 u.Delete(u,id) 会自动解析出需要删除的表然后根据Id删除数据.
```code
//删除我们根据Id删除
func (u *UsersModel) DeleteData(id int) bool {
if u.Delete(u, id).Error == nil {
return true
}
return false
}
```
### 4.批量删除(推荐使用第一种方法)
> 如果用户传递的参数是 ids 格式如右侧: "100,200,300,400"
```code
//批量删除
func (i *IptvUser) BatchDeleteData(ids string) bool {
if i.Where(" FIND_IN_SET(id,?)", ids).Delete(i).Error == nil {
go i.syncDelTbUsers(ids)
return true
}
return false
}
```
#### 5.批量删除数据(第二种方法)
> 如果用户传递的参数是 ids 格式如右侧: [100,200,300,400]
```code
//删除我们根据Id删除
func (u *UsersModel) DeleteData(ids []int) bool {
// ids 格式必须是: [100,200,300,400]
if u.Where("id in (?)",ids).Delete(u).Error == nil {
return true
}
return false
}
```
#### 6.查询
> 6.1 查询是sql操作最复杂的环节,如果业余复杂那么请使用原生sql操作业务
```code
// 查询类 sql 语句
u.Raw(sql语句,参数1,参数2... ... )
// 执行类 sql 语句
u.Exec(sql语句,参数1,参数2... ... )
```
> 6.2 接下来我们演示gorm自带查询
```code
// 第一种情况
// 如果 UsersModel 结构体已经绑定 TableName 函数,那么查询语句对应的数据表名就是 tableName 的返回值;
var tmp []UsersModel
// Where 关键词前面没有指定表名,那么查询的数据库表名就是 tmp 对应的结构体 UsersModel 结构体绑定的 TableName 的返回值
u.Where("ID = ?", user_id).Find(&tmp)
// 第二种情况
var tmp []UsersList
// 假设 UsersList 是自定义数据类型,没有绑定 TbaleName ,那么在 where 关键词开始时就必须指定表名
//指定表名 有以下两种方式:
// u.Model(u) 表示从 u 结构体绑定的 tableName 函数获取对应的表名,如果 u 对应的结构体和 tmp 对应的结构体 UsersList 都没有绑定 TableName ,就会发生错误
u.Model(u).Where("ID = ?", user_id).Find(&tmp)
// u.Tbale(u.TableName()).Where("ID = ?", user_id).Find(&tmp)
```