这是我参与「第五届青训营 」伴学笔记创作活动的第 1 天
GORM介绍
Gorm是go语言编写的一个ORM框架。
ORM框架的优缺点: 优点: 提高开发效率 缺点: 1.牺牲执行功能 2.牺牲灵活性 3.弱化SQL能力
GORM的安装:
go get -u gorm.io/gorm
go get -u gorm.io/driver/mysql
模型定义
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"`
}
常用的字段标签:
| 标签名 | 解释 |
|---|---|
| column | 指定列名 |
| type | 指定列数据类型,例如:bool、int、uint、float、string、time、bytes、varchar(),可与其他标签一块使用 |
| size | 指定列数据的大小和长度,例:size:256 |
| primaryKey | 将列定义为主键 |
| unique | 设置列的值是唯一的 |
| default | 设置列的默认值 |
| scale | 设置数值精度 |
| not null | 不为空 |
| embedded | 嵌入自己定义的结构体 |
| embeddedPrefix | 添加列名的前缀 |
| check | 设置check约束 |
| <- | 设置列是否可写入,<-:create 只能被创建, <-:update 只能被更新, <-:false 不可写入, <- 允许写和创建 |
| -> | 设置列是否可读,->:false 表示不可读 |
| - | 忽略该列,- 不允许读写, -:migration 不允许迁移, -:all 不允许读写和迁移 |
数据库的连接
db, err := gorm.Open(mysql.New(mysql.Config{
DSN: "gorm:gorm@tcp(127.0.0.1:3306)/gorm?charset=utf8&parseTime=True&loc=Local", // DSN data source name
DefaultStringSize: 256, // string 类型字段的默认长度
DisableDatetimePrecision: true, // 禁用 datetime 精度,MySQL 5.6 之前的数据库不支持
DontSupportRenameIndex: true, // 重命名索引时采用删除并新建的方式,MySQL 5.7 之前的数据库和 MariaDB 不支持重命名索引
DontSupportRenameColumn: true, // 用 `change` 重命名列,MySQL 8 之前的数据库和 MariaDB 不支持重命名列
SkipInitializeWithVersion: false, // 根据当前 MySQL 版本自动配置
}), &gorm.Config{
Logger: logger.Default.LogMode(logger.Silent), //设置日志格式
SkipDefaultTransaction: true, //跳过默认操作
DisableForeignKeyConstraintWhenMigrating: true,//不允许物理外键
NamingStrategy: schema.NamingStrategy{
SingularTable: true,
},//取消复数命名
})
sqlDB, err := db.DB()
// SetMaxIdleConns 设置空闲连接池中连接的最大数量
sqlDB.SetMaxIdleConns(10)
// SetMaxOpenConns 设置打开数据库连接的最大数量。
sqlDB.SetMaxOpenConns(100)
// SetConnMaxLifetime 设置了连接可复用的最大时间。
sqlDB.SetConnMaxLifetime(time.Hour)
//数据迁移
db.AutoMigrate(&User{})
数据库操作
创建记录
1.创建记录并给选定字段赋值
db.Select("Name","Age","CreatedAt").Create(&user)
2.批量创建
var users=[]User{user1,user2,...}
db.Create(&users)
3.根据map创建
db.Model(&User{}).Create(map[string]interface{}{
"Name": "zyj", "Age": 18,
})
db.Model(&User{}).Create([]map[string]interface{}{
{"Name": "zyj_1", "Age": 18},
{"Name": "zyj_2", "Age": 20},
})
4.关联创建
type CreditCard struct {
gorm.Model
Number string
UserID uint
}
type User struct {
gorm.Model
Name string
CreditCard CreditCard
}
db.Create(&User{
Name: "zyj",
CreditCard: CreditCard{Number: "411111111111"}
})
5.创建钩子
func (u *User) BeforeCreate(tx *gorm.DB) (err error) {
if u.Role == "admin" {
return errors.New("invalid role")
}
return
}
删除记录
1.给定数据删除,删除的对象需要指定主键
db.Delete(&email)
2.带条件删除
db.Where("name = ?", "zyj").Delete(&email)
3.根据主键删除
db.Delete(&User{}, 10)
4.查询被软删除的记录
db.Unscoped().Where("age = 20").Find(&users)
5.永久删除
db.Unscoped().Delete(&order)
6.删除钩子
func (u *User) BeforeDelete(tx *gorm.DB) (err error) {
if u.Role == "admin" {
return errors.New("admin user not allowed to delete")
}
return
}
修改记录
1.给定数据更新,更新的对象需要有主键
db.Model(&user).Update("name", "hello")
2.带条件的更新
db.Model(&User{}).Where("active = ?", true).Update("name", "hello")
3.更新多列
db.Model(&user).Updates(User{Name: "hello", Age: 18, Active: false})
db.Model(&user).Updates(map[string]interface{}{"name": "hello", "age": 18, "active": false})
4.批量更新
db.Model(User{}).Where("role = ?", "admin").Updates(User{Name: "hello", Age: 18})
5.嵌入SQL表达式更新
db.Model(&product).Update("price", gorm.Expr("price * ? + ?", 2, 100))
6.根据子查询更新
db.Model(&User).Update("company_name", db.Model(&Company{}).Select("name").Where("Company.id = User.company_id"))
7.更新钩子
func (u *User) BeforeUpdate(tx *gorm.DB) (err error) {
if u.Role == "admin" {
return errors.New("admin user not allowed to update")
}
return
}
查询记录
1.查询单个对象
db.First(&user)
2.查询多个对象
db.Find(&users)
3.用主键查询
db.First(&user,10)
db.Find(&users,[]int{1,2,3})
4.简单条件查询
db.Where("name = ?", "zyj").First(&user)
db.Where("name IN ?", []string{"zyj", "zyj2"}).Find(&users)
db.Where(&User{Name: "zyj", Age: 20}).First(&user)
db.Where(map[string]interface{}{"name": "zyj", "age": 20}).Find(&users)
db.Where([]int64{20, 21, 22}).Find(&users)
5.内联条件
db.Find(&users, "name = ?", "zyj")
db.Find(&users, User{Age: 20})
db.Find(&users, map[string]interface{}{"age": 20})
6.Not条件
db.Not("name = ?", "zyj").First(&user)
db.Not(map[string]interface{}{"name": []string{"zyj", "zyj 2"}}).Find(&users)
7.Or条件
db.Where("role = ?", "admin").Or("role = ?", "super_admin").Find(&users)
db.Where("name = 'jinzhu'").Or(User{Name: "zyj2", Age: 18}).Find(&users)
8.选择特定字段
db.Select("name", "age").Find(&users)
9.Order
db.Order("age desc, name").Find(&users)
10.Limit&Offset
db.Limit(10).Offset(5).Find(&users)
11.Group By&Having
type result struct {
Date time.Time
Total int
}
db.Model(&User{}).Select("name, sum(age) as total").Where("name LIKE ?", "group%").Group("name").First(&result)
db.Model(&User{}).Select("name, sum(age) as total").Group("name").Having("name = ?", "group").Find(&result)
12.Joins
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{})
db.Joins("JOIN emails ON emails.user_id = users.id AND emails.email = ?", "jinzhu@example.org").Joins("JOIN credit_cards ON credit_cards.user_id = users.id").Where("credit_cards.number = ?", "411111111111").Find(&user)
13.子查询
db.Where("amount > (?)", db.Table("orders").Select("AVG(amount)")).Find(&orders)
db.Where(
db.Where("pizza = ?", "pepperoni").Where(db.Where("size = ?", "small").Or("size = ?", "medium")),
).Or(
db.Where("pizza = ?", "hawaiian").Where("size = ?", "xlarge"),
).Find(&Pizza{})
14.查询钩子
func (u *User) AfterFind(tx *gorm.DB) (err error) {
if u.Role == "" {
u.Role = "user"
}
return
}
关联
type User struct {
gorm.Model
Username string
Orders []Order
}
type Order struct {
gorm.Model
UserID uint
Price float64
}
1. 查找 user 时预加载相关 Order
db.Preload("Orders").Find(&users)
2.带条件的预加载
db.Joins("Company", DB.Where(&Company{Alive: true})).Find(&users)
db.Preload("Orders", "state NOT IN (?)", "cancelled").Find(&users)
事务
db.Transaction(func(tx *gorm.DB) error {
// 在事务中执行一些 db 操作(从这里开始,您应该使用 'tx' 而不是 'db')
if err := tx.Create(&Animal{Name: "Giraffe"}).Error; err != nil {
// 返回任何错误都会回滚事务
return err
}
if err := tx.Create(&Animal{Name: "Lion"}).Error; err != nil {
return err
}
// 返回 nil 提交事务
return nil
})
// 开始事务
tx := db.Begin()
// 在事务中执行一些 db 操作(从这里开始,您应该使用 'tx' 而不是 'db')
tx.Create(...)
// ...
// 遇到错误时回滚事务
tx.Rollback()
// 否则,提交事务
tx.Commit()