这是我参与「第三届青训营 -后端场」笔记创作活动的第2篇笔记
GORM基础使用
功能:
- 关联:一对一,一对多,单表自关联、多态;Preload、Joins预加载、级联删除;关联模式;自定义关联表
- 事务:事务代码块、嵌套事务、Save Point
- 多数据库、读写分离、命名参数、Map、子查询、分组条件、代码共享、SQL表达式(查询、创建、更新)、自动选字段、查询优化器
- 字段权限、软删除、批量数据处理、Prepared Stmt、自定义类型、命名策略、虚拟字段、自动track时间、SQL Builder、Logger
- 代码生成、复合主键、Constraint、Prometheus、Auto Migration
- 多模式灵活自由扩展
- 开发友好
基本用法
初始化DB连接
import _"github.com/go-sql-driver/mysql"
func main(){
//通过driver+DSN初始化连接
db, err := sql.Open("mysql","user:password@tcp(127.0.0.1:3306)/hello")
}
CRUD
//操作数据库,依据go的结构体自动迁移在数据库中创建表
//表中的字段就是结构体内的元素
db.AutoMigrate(&Product{})
db.Migrator().CreateTable(&Product{})
//创建,注意传递的是地址,也就是执行结构体实例的指针
user := User{Name: "Tom", Age: 18, Birthday: time.Now()}
result := db.Create(&user)
//批量创建,可以通过CreateInBatches指定批次的数量
var users = []User{{Name: "Amy"}, {Name: "Bob"}}
db.Create(&users)
db.CreateInBatches(users, 100)
//读取 获取第一条记录(主键升序)
db.First(&user)
// SELECT * FROM users ORDER BY id LIMIT 1;
// 获取一条记录,没有指定排序字段
db.Take(&user)
// SELECT * FROM users LIMIT 1;
// 获取最后一条记录(主键降序)
db.Last(&user)
// SELECT * FROM users ORDER BY id DESC LIMIT 1;
//更新
// 条件更新
db.Model(&User{}).Where("active = ?",true).Update("name", "hello")
// UPDATE users SET name='hello', updated_at='2013-11-17 21:34:10' WHERE active=true;
db.Model(&user).Update("name", "hello")
// UPDATE users SET name='hello', updated_at='2013-11-17 21:34:10' WHERE id=111;
// 根据条件和 model 的值进行更新
db.Model(&user).Where("active = ?", true).Update("name", "hello")
// UPDATE users SET name='hello', updated_at='2013-11-17 21:34:10' WHERE id=111 AND active=true
//删除
db.Delete(&user)
注:传入的都是地址或指针
模型定义
gorm自带了Model模型的结构体定义,可以嵌入到任何结构体中,Model模型:
type Model struct{
ID uint `gorm:"primaryKey"`
CreatedAt time.Time
UpdatedAt time.Time
DeletedAt gorm.DeletedAt `gorm:"index"`
}
其中ID就是默认主键,全局唯一,会自增,DeletedAt是为了方便软删除,拥有软删除能力的模型调用 Delete 时,记录不会从数据库中被真正删除。但 GORM 会将 DeletedAt 置为当前时间, 并且你不能再通过普通的查询方法找到该记录。
可以使用 Unscoped 找到被软删除的记录
db.Unscoped().Where("age = 20").Find(&users)
// SELECT * FROM users WHERE age = 20;
也可以使用 Unscoped 永久删除匹配的记录
db.Unscoped().Delete(&order)
// DELETE FROM orders WHERE id=10;
模型定义的管理约定(约定优于配置)
- 表名为
struct name的snake_cases复数格式 - 字段名为
field name的snake_case单数格式 ID/Id字段为主键,如果为数字,则为自增主键CreatedAt字段,创建时,保存当前时间UpdateAt字段,创建、更新时保存当前时间gorm.DeletedAt字段,默认开启soft delete模式
关联
包含一对一,一对多(一般通过gorm设置外键tag实现),多对多(通过gorm设置一个tag来生成关联中间表)
说明:User拥有一个Account(has one,一对一),拥有多个Pets(has many,一对多),多个Toys(多态has many,User可以拥有Toy,Pet也可以拥有Toy),属于某Company(belongs to),属于某Manager(单表,belongs to),管理Team(单表,has many),会多种Language(manytomany,多对多),拥有很多Friend(单表,many to many)
type User struct{
gorm.Model
Name string
Account Account
Pets []*Pet
Toys []Toy `gorm:"polymorphic:Owner"` //polymorphic,多态的tag
CompanyID *int
Company Company
ManagerID *uint
Manager *User
//外键关联实现一对多,ManagerID作为外键
Team []User `gorm:"foreignkey:ManagerID"`
//many2many实现多对多,会生成一张userspeak中间表
Languages []Language `gorm:"many2many:UserSpeak;"`
Friends []*User `gorm:"many2many:user_friends;`
}
type Pet struct{
gorm.Model
UserID *uint
//多态
Toy Toy `gorm:"polymorphic:Owner;"`
}
type Toy struct{
ID uint
Name string
OwnerID string
OwnerType string
CreatedAt time.Time
}
关联操作--CRUD
保存用户及其关联
db.Save(&User{
Name: "Tom",
Languages: []Language{{Name: "zh-CN"}, {Name: "en-US"}},
})
关联模式
langAssociation := db.Model(&user).Association("Languages")
查询关联
langAssociation.Find(&languages)
将汉语,英语添加到用户掌握的语言中
langAssociation.Append([]Language{languageZH, languageEN})
把用户掌握的语言换为汉语,德语
langAssociation.Replace([]Language{languageZH, languageDE})
删除用户掌握的汉语和英语
langAssociation.Delete(languageZH, languageEN)
删除用户掌握的所有语言
langAssociation.Clear()
返回用户掌握的语言的数量
langAssociation.Count()
关联操作--Preload/Joins预加载
结构体
type User struct{
Orders []Order
Profile Profile
}
查询用户的时候并找出其订单和个人信息
db.Preload("Orders").Preload("Profile").Find(&user)
//SELECT * FROM users;
//SELECT * FROM orders WHERE user_id IN (1,2,3,4);//一对多
//SELECT * FROM profiles WHRER user_id IN (1,2,3,4);//一对一
使用join SQL加载
db.Joins("Company").Joins("Manager").First(&user, 1)
db.Joins("Company", db.Where(&Company{Alive: True})).Find(&user)
预加载全部关联(只加载一级关联)
db.Preload(clause.Associations).Find(&user)
多级预加载
db.Preload("Orders.OrderItems.Product").Find(&users)
多级预加载+预加载全部一级关联
db.Preload("Order.OrderItems.Product").Preload(clause.Associations).Find(&user)
关联操作--级联删除
结构体:
type User struct{
ID uint
Name string
Account Account `gorm:"constraint:OnUpdate:CASCADE,OnDelete:CASCADE;"`
CreditCards []CreditCard `gorm:"constraint:OnUpdate:CASCADE,OnDelete:CASCADE;"`
Orders []Order `gorm:"constraint:OnUpdate:CASCADE,OnDelete:CASCADE;"`
}
方法一:使用数据库约束自动删除
//如果未启动软删除,在删除User时会自动删除其依赖。
db.Delete(&User{})
方法二:使用Select实现级联删除,不依赖数据库约束及软删除
//删除user时也删除其相应的account
db.Select("Account").Delete(&user)
//删除user时,也删除user的Orders, CreditCards记录
db.Select("Orders", "CreditCards").Delete(&user)