GORM|青训营课程笔记

106 阅读4分钟

这是我参与「第五届青训营 」伴学笔记创作活动的第 1 天

GORM介绍

Gorm是go语言编写的一个ORM框架。

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()