Gin 中使用 GORM 操作 mysql 数据库
# 1、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…
每个特性都经过了测试的重重考验
开发者友好
官方文档:https://gorm.io/zh_CN/docs/index.html (opens new window)
# 2、Gin 中使用 GORM
1、安装
如果使用 go mod 管理项目的话可以忽略此步骤
go get -u gorm.io/gorm
go get -u gorm.io/driver/mysql
2、Gin 中使用 Gorm 连接数据库
在 models 下面新建 core.go ,建立数据库链接
package models
import (
"gorm.io/driver/mysql"
"gorm.io/gorm"
"log"
)
var DB *gorm.DB
var err error
func Init() {
dsn := "root:123456@tcp(127.0.0.1:3306)/gorm_test?charset=utf8mb4&parseTime=True&loc=Local"
DB, err = gorm.Open(mysql.Open(dsn), &gorm.Config{})
if err != nil {
log.Fatal("数据库连接失败:", err)
}
// 自动创建表
err = DB.AutoMigrate(&User{})
if err != nil {
log.Fatal("自动建表失败:", err)
}
log.Println("数据库表已创建或同步成功")
}
3、定义操作数据库的模型
Gorm 官方给我们提供了详细的:https://gorm.io/zh_CN/docs/models.html (opens new window)
虽然在 gorm 中可以指定字段的类型以及自动生成数据表,但是在实际的项目开发中,我们是先设计数据库表,然后去实现编码的。
在实际项目中定义数据库模型注意以下几点:
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 models
// User 默认表名是 `users`
type User struct {
Id int `gorm:"primaryKey;autoIncrement"` // ID,主键
Username string `gorm:"size:50;not null;unique" form:"username" json:"username"` // 用户名,唯一
Age int `form:"age" json:"age"` // 年龄
Email string `gorm:"size:100" form:"email" json:"email"` // 邮箱地址
AddTime int // 添加时间(Unix 时间戳)
}
func (User) TableName() string {
return "user"
}
关于更多模型定义的方法参考:https://gorm.io/zh_CN/docs/conventions.html (opens new window)
gorm.Model
GORM 定义一个 gorm.Model 结构体,其包括字段 ID、CreatedAt、UpdatedAt、DeletedAt
// gorm.Model 的定义
type Model struct {
ID uint `gorm:"primaryKey"`
CreatedAt time.Time
UpdatedAt time.Time
DeletedAt gorm.DeletedAt `gorm:"index"`
}
# 3、Gin GORM CURD
找到要操作数据库表的控制器,然后引入 models 模块
# 3.1、增加
增加成功后会返回刚才增加的记录
func (con UserController) Add(c *gin.Context) {
var user models.User
// 绑定参数(form、query 等)
if err := c.ShouldBind(&user); err != nil {
c.String(http.StatusBadRequest, "参数绑定失败: %v", err)
return
}
//user := models.User{
// Username: "王五", Age: 18, Email: "1179732961@qq.com", AddTime: int(time.Now().Unix())}
result := models.DB.Create(&user) // 通过数据的指针来创建
if result.RowsAffected > 1 {
fmt.Print(user.Id)
}
fmt.Println(result.RowsAffected)
fmt.Println(user.Id)
c.String(http.StatusOK, "添加成功,ID: %d", user.Id)
}
更多增加语句:https://gorm.io/zh_CN/docs/create.html (opens new window)
# 3.2、查找
查找全部
func (con UserController) SelectAll(c *gin.Context) {
user := []models.User{}
models.DB.Find(&user)
c.JSON(http.StatusOK, gin.H{"success": true, "result": user})
}
指定条件查找
func (con UserController) QueryByParam(c *gin.Context) {
user := []models.User{}
models.DB.Where("username=?", "王五").Find(&user)
c.JSON(http.StatusOK, gin.H{"success": true, "result": user})
}
更多查询语句:https://gorm.io/zh_CN/docs/query.html (opens new window)
# 3.3、修改
func (con UserController) Edit(c *gin.Context) {
user := models.User{Id: 7}
models.DB.Find(&user)
user.Username = "gin gorm"
user.Age = 1
models.DB.Save(&user)
c.String(http.StatusOK, "Edit")
}
更多修改的方法参考:https://gorm.io/zh_CN/docs/update.html (opens new window)
# 3.4、删除
func (con UserController) Delete(c *gin.Context) {
user := models.User{Id: 8}
models.DB.Delete(&user)
c.String(http.StatusOK, "Delete")
}
更多删除的方法参考:https://gorm.io/zh_CN/docs/delete.html (opens new window)
# 3.5、批量删除
db.Where("email LIKE ?", "%jinzhu%").Delete(Email{})
// DELETE from emails where email LIKE "%jinzhu%";
db.Delete(Email{}, "email LIKE ?", "%jinzhu%")
// DELETE from emails where email LIKE "%jinzhu%";
func (con UserController) DeleteAll(c *gin.Context) {
user := models.User{}
models.DB.Where("id>9").Delete(&user)
c.String(http.StatusOK, "DeleteAll")
}
更多删除的方法参考:https://gorm.io/zh_CN/docs/delete.html (opens new window)
# 4、Gin GORM 查询语句详解
官方文档:https://gorm.io/zh_CN/docs/query.html (opens new window)
# 4.1、Where
=
<
nav := []models.Nav{} models.DB.Where("id<3").Find(&nav) c.JSON(http.StatusOK, gin.H{"success": true, "result": nav})
>
var n = 5 nav := []models.Nav{} models.DB.Where("id>?", n).Find(&nav) c.JSON(http.StatusOK, gin.H{"success": true, "result": nav})
<=
>=
!=
IS NOT NULL
IS NULL
BETWEEN AND
nav := []models.Nav{} models.DB.Where("id between ? and ?", 3, 6).Find(&nav) c.JSON(http.StatusOK, gin.H{"success": true, "result": nav})
NOT BETWEEN AND
IN
nav := []models.Nav{} models.DB.Where("id in (?)", []int{3, 5, 6}).Find(&nav) c.JSON(http.StatusOK, gin.H{"success": true, "result": nav})
OR
AND
var n1 = 3 var n2 = 9 nav := []models.Nav{} models.DB.Where("id > ? AND id < ?", n1, n2).Find(&nav) c.JSON(http.StatusOK, gin.H{ "success": true, "result": nav})
NOT
LIKE
nav := []models.Nav{} models.DB.Where("title like ?", "%会%").Find(&nav) c.JSON(http.StatusOK, gin.H{"success": true, "result": nav})
# 4.2、Or 条件
nav := []models.Nav{}
models.DB.Where("id=? OR id=?", 2, 3).Find(&nav)
或者
nav := []models.Nav{}
models.DB.Where("id=?", 2).Or("id=?", 3).Or("id=4").Find(&nav)
# 4.3、选择字段查询
nav := []models.Nav{}
models.DB.Select("id, title,url").Find(&nav)
# 4.4、排序 Limit 、Offset
nav := []models.Nav{}
models.DB.Where("id>2").Order("id Asc").Find(&nav)
nav := []models.Nav{}
models.DB.Where("id>2").Order("sort Desc").Order("id Asc").Find(&nav)
nav := []models.Nav{}
models.DB.Where("id>1").Limit(2).Find(&nav)
跳过 2 条查询 2 条
nav := []models.Nav{}
models.DB.Where("id>1").Offset(2).Limit(2).Find(&nav)
# 4.5、获取总数
nav := []models.Nav{}
var num int64
models.DB.Where("id > ?", 2).Find(&nav).Count(&num)
# 4.6、Distinct
从模型中选择不相同的值
nav := []models.Nav{}
models.DB.Distinct("title").Order("id desc").Find(&nav)
c.JSON(200, gin.H{ "nav": nav, })
等价SQL
SELECT DISTINCT `title` FROM `nav` ORDER BY id desc
# 4.7、Scan
type Result struct {
Name string
Age int
}
var result Result
db.Table("users").Select("name", "age").Where("name = ?", "Antonio").Scan(&result)
// 原生 SQL
db.Raw("SELECT name, age FROM users WHERE name = ?", "Antonio").Scan(&result)
var result []models.User
models.DB.Raw("SELECT * FROM user").Scan(&result)
fmt.Println(result)
# 4.8、Join (先了解 后面课程会讲关联查询)
type result struct {
Name string
Email string
}
db.Model(&User{}).Select("users.name, emails.email").Joins("left join emails on emails.user_id = users.id").Scan(&result{})
等同SQL
SELECT users.name, emails.email FROM `users` left join emails on emails.user_id = users.id
# 5、Gin GORM 查看执行的 sql
package models
import (
"gorm.io/driver/mysql"
"gorm.io/gorm"
"gorm.io/gorm/logger"
"log"
)
var DB *gorm.DB
var err error
func Init() {
dsn := "root:123456@tcp(127.0.0.1:3306)/gorm_test?charset=utf8mb4&parseTime=True&loc=Local"
DB, err = gorm.Open(mysql.Open(dsn), &gorm.Config{
QueryFields: true, // 查询语句中会显式列出所有字段名
Logger: logger.Default.LogMode(logger.Info), // 打印完整 SQL
})
if err != nil {
log.Fatal("数据库连接失败:", err)
}
// 自动创建表
err = DB.AutoMigrate(&User{})
if err != nil {
log.Fatal("自动建表失败:", err)
}
log.Println("数据库表已创建或同步成功")
//DB.Debug() 临时打印执行sql
}
效果如下: