GORM 操作 Mysql 数据库(一)
温馨提示:以下关于 GORM 的使用,是基于 Gin 框架的基础上,如果之前没有了解过 Gin 可能略微困难。
GORM 介绍
GORM 是 Golang 的一个 orm 框架。简单说,ORM 就是通过实例对象的语法,完成关系型
数据库的操作的技术,是"对象-关系映射"(Object/Relational Mapping) 的缩写。使用 ORM
框架可以让我们更方便的操作数据库。
GORM 官方支持的数据库类型有: MySQL, PostgreSQL, SQlite, SQL Server。
GORM 特性
- 全功能 ORM
- 关联 (Has One,Has Many,Belongs To,Many To Many,多态,单表继承)
- Create,Save,Update,Delete,Find 中钩子方法
- 支持 Preload、Joins 的预加载
- 事务,嵌套事务,Save Point,Rollback To Saved Point
- Context、预编译模式、DryRun 模式
- 批量插入,FindInBatches,Find/Create with Map,使用 SQL 表达式、Context Valuer 进
行 CRUD - SQL 构建器,Upsert,数据库锁,Optimizer/Index/Comment Hint,命名参数,子查询
- 复合主键,索引,约束
- Auto Migration
- 自定义 Logger
- 灵活的可扩展插件 API:Database Resolver(多数据库,读写分离)、Prometheus…
- 每个特性都经过了测试的重重考验
- 开发者友好
GORM 前置
安装
如果使用 go mod 管理项目的话可以忽略此步骤
go get -u gorm.io/gorm
go get -u gorm.io/driver/mysql
连接数据库
在 models 下面新建 core.go ,建立数据库链接
package modles
import (
"gorm.io/driver/mysql"
"gorm.io/gorm"
)
var db *gorm.DB
var err error
func init() {
dsn := "root:123456789@tcp(127.0.0.1:3306)/gorm_test?charset=utf8mb4&parseTime=True&loc=Local"
// Globally mode
db, err = gorm.Open(mysql.Open(dsn), &gorm.Config{})
if err != nil {
panic(err)
}
}
定义操作模型
在实际项目中定义数据库模型注意以下几点:
1、结构体的名称必须首字母大写 ,并和数据库表名称对应。
例如:表名称为 user
结构体名称定义成 User
,表名称为 article_cate
结构体名称定义成 ArticleCate
。
2、结构体中的字段名称首字母必须大写,并和数据库表中的字段一一对应。
例如:下面结 构体中的 Id
和数据库中的 id
对应,Username
和数据库中的 username
对应,Age
和数据库中 的 age
对应,Email
和数据库中的 email
对应,AddTime
和数据库中的 add_time
字段对应。
3、默认情况表名是结构体名称的复数形式。
如果我们的结构体名称定义成 User
,表示这个 模型默认操作的是 users
表。
4、我们可以使用结构体中的自定义方法 TableName
改变结构体的默认表名称,如下:
func (User) TableName() string {
return "user"
}
表示把 User
结构体默认操作的表改为 user
表。
定义 User
模型:
package modles
type User struct {
// 默认表名是 users
Id int
Username string
Age int1
Email string
AddTime int
}
func(User) TableName() string {
return "user"
}
gorm.Model
模型
GORM 定义一个 gorm.Model 结构体,其包括字段 ID
、CreatedAt
、UpdatedAt
、DeletedAt
。
type Model struct {
ID uint `gorm:"primaryKey"`
CreatedAt time.Time
UpdatedAt time.Time
DeletedAt gorm.DeletedAt `gorm:"index"`
}
GORM CRUD
注意:我们在关键词之前加上 DEBUG
方法可以获取执行的 SQL 语句。
增加
我们一般使用 Create
方法构建,增加成功后会返回刚才增加的记录。
func (con UserController) Add(c *gin.Context) {
// 单数据
user := models.User{
UserName: "ypb",
Age: 20,
Email: "ychag@example.com",
AddTime: int(time.Now().Unix()),
}
// 批量数据
users := []*models.User{
{UserName: "yxc", Age: 20, Email: "ychag@example.com", AddTime: int(time.Now().Unix())},
{UserName: "zmz", Age: 20, Email: "zmzag@example.com", AddTime: int(time.Now().Unix())},
}
result := models.DB.Create(&users) // 通过指针创建
if result.RowsAffected > 1 {
fmt.Println(user.Id)
}
fmt.Println(result.RowsAffected)
fmt.Println(user.Id)
c.String(200, "add ok")
}
查找
GORM 提供了 First
、Take
、Last
、Find
方法,以便从数据库中检索对象。
不指定条件查询
func (con UserController) Get(c *gin.Context) {
user := models.User{}
// First 获取第一条记录(主键升序)
models.DB.Debug().First(&user)
// SELECT * FROM `user` ORDER BY `user`.`id` LIMIT 1
fmt.Println(user.Id)
// Last 获取最后一条记录(主键降序)
models.DB.Debug().Last(&user)
fmt.Println(user.Id)
// SELECT * FROM `user` WHERE `user`.`id` = 1 ORDER BY `user`.`id` DESC LIMIT 1
// Find 获取所有记录
users := []models.User{}
models.DB.Debug().Find(&users)
//SELECT * FROM `user`
c.JSON(200, gin.H{
"message": "get ok",
"result": users,
})
}
指定条件查询
func (con UserController) GetByCondition(c *gin.Context) {
user := models.User{}
// Where 指定查询条件
models.DB.Debug().Where("user_name = ?", "yxc").First(&user)
// SELECT * FROM `user` WHERE user_name = 'yxc' ORDER BY `user`.`id` LIMIT 1
c.JSON(200, gin.H{
"message": "get ok",
"result": user,
})
}
修改
字段条件查询修改
func (con UserController) Update(c *gin.Context) {
// 首先获取数据
user := models.User{Id: 1}
models.DB.Debug().Find(&user)
// SELECT * FROM `user` WHERE `user`.`id` = 1
// 然后更新数据
user.UserName = "haha"
user.Age = 22
models.DB.Debug().Save(&user)
// UPDATE `user` SET `user_name`='haha',`age`=22,`email`='ychag@example.com',`add_time`=1708047687 WHERE `id` = 1
c.String(200, "update ok")
}
where 条件查询修改
func (con UserController) UpdateByCondition(c *gin.Context) {
// Where 条件更新单列数据
models.DB.Debug().Model(&models.User{}).Where("id = ?", 1).Update("age", 15)
// UPDATE `user` SET `age`=15 WHERE id = 1
// Where 条件更新多列数据
var users []models.User
models.DB.Debug().Model(&users).Where("user_name = ?", "yxc").Updates(models.User{UserName: "yxc", Age: 22})
// UPDATE `user` SET `user_name`='yxc',`age`=22 WHERE user_name = 'yxc'
c.String(200, "update ok")
}
删除
删除单行数据:
func (con UserController) Delete(c *gin.Context) {
// Where 条件删除单条数据
models.DB.Debug().Where("user_name = ?", "haha").Delete(&models.User{})
// DELETE FROM `user` WHERE user_name = 'haha'
}
删除所有数据:
func (con UserController) DeleteAll(c *gin.Context) {
users := []models.User{}
models.DB.Debug().Where("id > ?", 0).Delete(&users)
// SELECT * FROM `user` WHERE id > 0
c.JSON(200, gin.H{
"message": "delete ok",
})
}
GORM 查询语句详解
where 及 操作符
func (a ArticleController) Get(c *gin.Context) {
article := &[]models.Article{}
// < 小于号, 选择所有id小于3的文章
models.DB.Debug().Where("id < ?", 3).Find(&article)
// SELECT * FROM `articles` WHERE id < 3 AND `articles`.`deleted_at` IS NULL
// > 大于号, 选择所有id大于5的文章
var id = 5
models.DB.Debug().Where("id > ?", id).Find(&article)
// SELECT * FROM `articles` WHERE id > 5 AND `articles`.`deleted_at` IS NULL
// AND 操作符, 选择所有id大于1且小于5的文章
models.DB.Debug().Where("id > ? and id < ?", 1, 5).Find(&article)
// SELECT * FROM `articles` WHERE (id > 1 AND id < 5) AND `articles`.`deleted_at` IS NULL
// in 操作符, 选择所有id大于2且小于5的文章
models.DB.Debug().Where("id in ?", []int{3, 4}).Find(&article)
//SELECT * FROM `articles` WHERE id in (3,4) AND `articles`.`deleted_at` IS NULL
// like 操作符, 选择 文章标题含 go 的文章
models.DB.Debug().Where("title like ?", "%go%").Find(&article)
// SELECT * FROM `articles` WHERE title like '%go%' AND `articles`.`deleted_at` IS NULL
// between and 操作符, 选择文章id在 1 到 5 之间的文章
models.DB.Debug().Where("id between ? and ?", 1, 5).Find(&article)
// SELECT * FROM `articles` WHERE (id between 1 and 5) AND `articles`.`deleted_at` IS NULL
// or 操作符, 选择文章id在 1 到 5 之间的文章或 id 等于 6 的文章
models.DB.Debug().Where("id between ? and ? or id = ?", 1, 5, 6).Find(&article)
//SELECT * FROM `articles` WHERE (id between 1 and 5 or id = 6) AND `articles`.`deleted_at` IS NULL
models.DB.Debug().Where("id between ? and ?", 1, 5).Or("id = ?", 6).Find(&article)
//SELECT * FROM `articles` WHERE ((id between 1 and 5) OR id = 6) AND `articles`.`deleted_at` IS NULL
c.JSON(200, gin.H{
"success": true,
"result": article,
})
}
指定字段查询
func (a ArticleController) GetOne(c *gin.Context) {
article := &[]models.Article{}
// 查询文章的id 和 title
models.DB.Debug().Select("id, title").First(&article)
c.JSON(200, gin.H{
"success": true,
"result": article,
})
}
排序
func (a ArticleController) GetOne(c *gin.Context) {
article := &[]models.Article{}
// 按 ID 降序排序
models.DB.Debug().Order("id desc").Find(&article)
// SELECT * FROM `articles` WHERE `articles`.`deleted_at` IS NULL ORDER BY id desc
// 按 ID 升序排序 按 author_id 降序排序
models.DB.Debug().Order("id asc, author_id desc").Find(&article)
// SELECT * FROM `articles` WHERE `articles`.`deleted_at` IS NULL ORDER BY id asc, author_id desc
c.JSON(200, gin.H{
"success": true,
"result": article,
})
}
Limit, Offset
func (a ArticleController) GetOne(c *gin.Context) {
article := &[]models.Article{}
// 查询ID大于1的文章, 限制1条数据 (随机一条)
models.DB.Debug().Where("id > ?", 1).Limit(1).Find(&article)
// SELECT * FROM `articles` WHERE id > 1 AND `articles`.`deleted_at` IS NULL LIMIT 1
// 实现 跳过一条数据查询2条数据 (分页)
models.DB.Debug().Where("id > ?", 1).Offset(1).Limit(1).Find(&article)
// SELECT * FROM `articles` WHERE id > 1 AND `articles`.`deleted_at` IS NULL LIMIT 1 OFFSET 1
c.JSON(200, gin.H{
"success": true,
"result": article,
})
}
获取总数
func (a ArticleController) GetSum(c *gin.Context) {
article := &[]models.Article{}
var sum int64
// 获取author_id等于1的文章的数量
models.DB.Debug().Where("author_id = ?", 1).Find(&article).Count(&sum)
//SELECT count(*) FROM `articles` WHERE author_id = 1 AND `articles`.`deleted_at` IS NULL
c.JSON(200, gin.H{
"success": true,
"result": article,
})
}
、
获取不同值
func (a ArticleController) GetDistinct(c *gin.Context) {
article := &[]models.Article{}
// 选择不同的 title
models.DB.Debug().Distinct("title").Find(&article)
// SELECT DISTINCT `title` FROM `articles` WHERE `articles`.`deleted_at` IS NULL
c.JSON(200, gin.H{
"success": true,
"result": article,
})
}
Scan
Scan 可以将结果绑定到一个结构体上。
type Result struct {
AuthorId int
Title string
}
func (a ArticleController) GetScan(c *gin.Context) {
var result []Result
//models.DB.Table("articles").Select("author_id, title").Scan(&result)
models.DB.Raw("SELECT author_id, title FROM articles").Scan(&result)
fmt.Println(result)
}
原生 SQL 和 生成器
1、使用原生 sql 删除 user 表中的一条数据
result := models.DB.Exec("delete from user where id=?", 3)
fmt.Println(result.RowsAffected)
2、使用原生 sql 修改 user 表中的一条数据
result := models.DB.Exec("update user set username=? where id=2", "哈哈")
fmt.Println(result.RowsAffected)
3、查询 uid = 2 的数据
var result models.User
models.DB.Raw("SELECT * FROM user WHERE id = ?", 2).Scan(&result)
fmt.Println(result)
4、查询 User 表中所有的数据
var result []models.User
models.DB.Raw("SELECT * FROM user").Scan(&result)
fmt.Println(result)
5、统计 user 表的数量
var count int
row := models.DB.Raw("SELECT count(1) FROM user").Row(&count )
row.Scan(&count)
结尾
本次我们了解了 GORM 的基本用法,主要设计到对一个表的操作。但是在实际的业务中,我们往往需要从多张表获取数据。
下次,我们将去了解 GORM 中多表操作。
下次见。