# 简单介绍
GORM (opens new window) 官方支持的数据库类型有: MySQL, PostgreSQL, SQlite, SQL Server
# 使用 GORM
- 安装
go get -u gorm.io/gorm
go get -u gorm.io/driver/mysql
# 使用流程
- 在 models 下面新建 core.go ,建立数据库链接
package models
import (
"fmt"
"gorm.io/driver/mysql"
"gorm.io/gorm"
)
var DB *gorm.DB
var err error
func init() {
dsn := "root:123456@tcp(127.0.0.1:3306)/gin?charset=utf8mb4&parseTime=True&loc=LLocal"
DB, err = gorm.Open(mysql.Open(dsn), &gorm.Config{
// false(默认):SELECT * ; true:SELECT id, name, age, ...
QueryFields: true,
// 禁用事务将获得大约 30%+ 性能提升
SkipDefaultTransaction: true,
})
db = db.Debug() // 查看执行的sql
if err != nil {
fmt.Println(err)
}
}
- 定义操作数据库的模型
// 结构体的名称必须首字母大写 ,并和数据库表名称对应
// 结构体中的字段名称首字母必须大写,并和数据库表中的字段一一对应
// 默认情况表名是结构体名称的复数形式。如果结构体名称定义成User,则模型默认操作的是 users 表
type User struct {
ID uint
Name string
Email *string
Age uint8
Birthday *time.Time
MemberNumber sql.NullString
ActivatedAt sql.NullTime
CreatedAt time.Time
UpdatedAt time.Time
ignored string
}
// 可以使用结构体中的自定义方法 TableName 改变结构体的默认表名称
// 使用匿名接收器,方法内部无法访问接收器对应的值,即不能使用 User 类型的字段或方法
func (User) TableName() string {
return "user" // 把 User 结构体默认操作的表改为 user 表
}
- 预定义的结构体 gorm.Model
// 可以直接在您的结构体中嵌入 gorm.Model
type Model struct {
ID uint
CreatedAt time.Time
UpdatedAt time.Time
DeletedAt gorm.DeletedAt
}
# CURD
# 增加、修改、删除
- 增加成功后会返回刚才增加的记录
func (con UserController) Add(c *gin.Context) {
user := models.User{
Username: "sylone",
Age: 18,
Email: "123@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, "add 成功")
}
- 修改
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")
}
- 删除
func (con UserController) Delete(c *gin.Context) {
user := models.User{Id: 8}
models.DB.Delete(&user)
c.String(http.StatusOK, "Delete")
}
- 批量删除
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")
}
- 修改
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")
}
# 查询语句
- 查找全部
func (con UserController) Index(c *gin.Context) {
user := []models.User{}
models.DB.Find(&user)
c.JSON(http.StatusOK, gin.H{
"success": true,
"result": user,
})
}
- 指定条件查找
user := []models.User{}
models.DB.Where("username=?", "王五").Find(&user)
c.JSON(http.StatusOK, gin.H{
"success": true,
"result": user,
})
var n = 5
nav := []models.Nav{}
models.DB.Where("id>?", n).Find(&nav)
c.JSON(http.StatusOK, gin.H{
"success": true,
"result": nav,
})
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,
})
nav := []models.Nav{}
models.DB.Where("id in (?)", []int{3, 5, 6}).Find(&nav)
c.JSON(http.StatusOK, gin.H{
"success": true,
"result": nav,
})
nav := []models.Nav{}
models.DB.Where("title like ?", "%会%").Find(&nav)
c.JSON(http.StatusOK, gin.H{
"success": true,
"result": nav,
})
nav := []models.Nav{}
models.DB.Where("id between ? and ?", 3, 6).Find(&nav)
c.JSON(http.StatusOK, gin.H{
"success": true,
"result": nav,
})
- 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)
- 选择字段查询
nav := []models.Nav{}
models.DB.Select("id, title,url").Find(&nav)
- 排序 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{}
odels.DB.Where("id>1").Limit(2).Find(&nav)
// 跳过 2 条查询 2 条
nav := []models.Nav{}
models.DB.Where("id>1").Offset(2).Limit(2).Find(&nav)
- 获取总数
nav := []models.Nav{}
var num int
models.DB.Where("id > ?", 2).Find(&nav).Count(&num)
- Distinct:从模型中选择不相同的值
nav := []models.Nav{}
models.DB.Distinct("title").Order("id desc").Find(&nav)
c.JSON(200, gin.H{ "nav": nav, })
- 将查询结果直接映射到指定的变量或结构体:Scan
# Find 需要基于模型(Model)操作,默认会映射到模型的全部字段
# Scan 更灵活,可以任意定义目标结构体,不依赖模型,适合复杂查询或非模型数据
// 原生 SQL 扫描到自定义结构体
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)
# 原生SQL
- 使用原生 sql 删除 user 表中的一条数据
result := models.DB.Exec("delete from user where id=?", 3)
fmt.Println(result.RowsAffected)
- 使用原生 sql 修改 user 表中的一条数据
result := models.DB.Exec("update user set username=? where id=2", "哈哈")
fmt.Println(result.RowsAffected)
- 查询 uid=2 的数据
var result models.User
models.DB.Raw("SELECT * FROM user WHERE id = ?", 2).Scan(&result)
fmt.Println(result)
- 查询 User 表中所有的数据
var result []models.User
models.DB.Raw("SELECT * FROM user").Scan(&result)
fmt.Println(result)
- 统计 user 表的数量
var count int
row := models.DB.Raw("SELECT count(1) FROM user").Row(&count )
row.Scan(&count)
# 关联查询
# 一对一
type Article struct {
Id int `json:"id"`
Title string `json:"title"`
Description int `json:"description"`
CateId string `json:"cate_id"` // 如果是ArticleCateId可以不用以下设置
State int `json:"state"`
// foreignkey 指定当前表的外键、references 指定关联表中和外键关联的字段
ArticleCate ArticleCate `gorm:"foreignKey:CateId;references:Id"`
}
type ArticleCate struct {
Id int `json:"id"`
Title string `json:"title"`
State int `json:"state"`
}
- 查询所有文章以及文章对应的分类信息
var articleList []models.Article
models.DB.Preload("ArticleCate").Limit(2).Find(&articleList)
c.JSON(200, gin.H{ "result": articleList, })
- 查询所有文章以及文章对应的分类信息 指定条件
var articleList []models.Article
models.DB.Preload("ArticleCate").Where("id>=?", 4).Find(&articleList)
c.JSON(200, gin.H{ "result": articleList, })
# 一对多
type ArticleCate struct {
Id int `json:"id"`
Title string `json:"title"`
State int `json:"state"`
Article []Article `gorm:"foreignKey:CateId"`
}
type Article struct {
Id int `json:"id"`
Title string `json:"title"`
Description int `json:"description"`
CateId string `json:"cate_id"`
State int `json:"state"`
}
- 查找所有分类以及分类下面的文章信息
var articleCateList []models.ArticleCate
models.DB.Preload("Article").Find(&articleCateList)
c.JSON(200, gin.H{ "result": articleCateList, })
- 查找所有分类以及分类下面的文章信息 指定条件
var articleCateList []models.ArticleCate
models.DB.Preload("Article").Where("id>0")Limit(1).Find(&articleCateList)
c.JSON(200, gin.H{ "result": articleCateList, })
# 多对多
type Lesson struct {
Id int `json:"id"`
Name string `json:"name"`
Student []*Student `gorm:"many2many:lesson_student"`
}
type Student struct {
Id int
Number string
Password string
ClassId int
Name string
Lesson []*Lesson `gorm:"many2many:lesson_student"`
}
type LessonStudent struct {
LessonId int // 建议这样命名,否则很麻烦
StudentId int // 建议这样命名,否则很麻烦
}
- 查询学生信息的时候获取学生的选课信息
studentList := []models.Student{}
models.DB.Preload("Lesson").Find(&studentList)
c.JSON(http.StatusOK, studentList)
- 查询张三选修了哪些课程
studentList := []models.Student{}
models.DB.Preload("Lesson").Where("id=1").Find(&studentList)
c.JSON(http.StatusOK, studentList)
- 课程被哪些学生选修了
lessonList := []models.Lesson{}
models.DB.Preload("Student").Find(&lessonList)
c.JSON(http.StatusOK, lessonList)
- 计算机网络被那些学生选修了
lessonList := []models.Lesson{}
models.DB.Preload("Student").Where("id=1").Find(&lessonList)
c.JSON(http.StatusOK, lessonList)
- 查询数据指定条件
lessonList := []models.Lesson{}
models.DB.Preload("Student").Offset(1).Limit(2).Find(&lessonList)
c.JSON(http.StatusOK, lessonList)
# 关联查询指定子集的筛选条件
- 张三被开除了 查询课程被哪些学生选修的时候要去掉张三
lessonList := []models.Lesson{}
models.DB.Preload("Student", "id!=1").Find(&lessonList)
c.JSON(http.StatusOK, lessonList)
lessonList := []models.Lesson{}
models.DB.Preload("Student", "id not in (1,2)").Find(&lessonList)
c.JSON(http.StatusOK, lessonList)
# 自定义预加载 SQL
- 查看课程被哪些学生选修 要求:学生 id 倒叙输出
lessonList := []models.Lesson{}
models.DB.Preload("Student", func(db *gorm.DB) *gorm.DB {
return models.DB.Where("id>3").Order("id DESC")
}).Find(&lessonList)
c.JSON(http.StatusOK, lessonList)
# 使用事务
- 事务(手动控制)
// 开启事务
tx := db.Begin()
// 在事务中做一些数据库操作 (这里应该使用 'tx' ,而不是 'db')
tx.Create(...)
// ... // 有错误时,手动调用事务的 Rollback()
tx.Rollback()
// 无错误时,手动调用事务的 Commit()
tx.Commit()
- 张三给李四转账
type TransitionController struct {
BaseController
}
func (con TransitionController) Index(c *gin.Context) {
tx := models.DB.Begin()
defer func() {
if r := recover(); r != nil {
tx.Rollback()
con.Error(c)
}
}()
if err := tx.Error; err != nil {
fmt.Println(err)
con.Error(c)
}
// 张三账户减去 100
u1 := models.Bank{Id: 1}
tx.Find(&u1)
u1.Balance = u1.Balance - 100
if err := tx.Save(&u1).Error; err != nil {
tx.Rollback()
con.Error(c)
}
// panic("遇到了错误")
// 李四账户增加 100
u2 := models.Bank{Id: 2}
tx.Find(&u2)
u2.Balance = u2.Balance + 100
// panic("失败")
if err := tx.Save(&u2).Error; err != nil {
tx.Rollback()
con.Error(c)
}
tx.Commit()
con.success(c)
}
← 基础知识