go语言的魔幻旅程30-orm框架之Gorm

欲穷千里目,更上一层楼

Gorm相关的知识总体上而言不是很困难,从某种程度上来讲,只要扎扎实实的掌握了sql相关的语法,针对gorm的学习已经成功了50%, 剩下的部分无外乎是运用属于gorm语言的方式将sql语句进行恰当的表达,这个部分可能需要一定的时间来掌握,总体上来讲个人认为gorm这个框架设计的比较的蹩脚,接下来我将就采用mysql数据库的案例针对gorm展开总结。


Gorm基本语法

1、MySQL的连接

gorm针对MySQL的连接需要两个包,分别为gorm包、MySQL包,获取的方式如下:

go get -u  gorm.io/driver/mysql
go get -u gorm.io/gorm

安装完驱动包之后,就可以向其他编程语言一样针对MySQL连接做一些配置,当然也可以采用纯净版直接连接,去除多余连接配置


//纯净版连接
dsn := "user:pass@tcp(127.0.0.1:3306)/dbname?charset=utf8mb4&parseTime=True&loc=Local"
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})

//多余版本配置
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{})


2、gorm针对数据操作时对应表名称的控制

在其他的编程语言中,向那张表中插入数据,开发人员有绝对的自由,然而在gorm这个框架中开发人员如果想随心所欲的往数据库中插入数据还需要做一些加工,如果不做任何的处理,默认的情况下是gorm使用结构体的蛇形命名作为表的名称,例如如果结构体名称为User,按照约定插入时表明为users,如果不想采用蛇形复数的形式,那么可以采用全局禁用复数的形式:db.SingularTable(true),如果前两者都不能满足你的需求,那么还可以通过重新结构体的TableName方法来达到目的。

func (s StructType) TableName() string {
    return "tableName"
}

//例如,User结构体我想把数据插入到xxx表可以这样操作
func (u User) TableName() string {
    return "xxx"
}

3、gorm中针对结构体字段的标签

column => 指定db列名称

type => 列数据类型,推荐使用兼容性好的通用类型,例如:所有数据库都支持 bool、int、uint、float、string、time、bytes 并且可以和其他标签一起使用,例如:not null、size, autoIncrement… 像 varbinary(8) 这样指定数据库数据类型也是支持的。在使用指定数据库数据类型时,它需要是完整的数据库数据类型,如:MEDIUMINT UNSIGNED not NULL AUTO_INCREMENT

size => 指定列的大小,例如size:256

primaryKey => 指定列为主键

unique => 指定列为唯一

default => 指定列的默认值

precision => 指定列的精度

scale => 指定列的大小

not null => 指定列为NOT NULL

autoIncrement => 指定列为自动增长

autoIncrementIncrement => 自动步长,控制连续记录之间的间隔

embedded => 嵌套字段

embeddedPrefix => 嵌入字段的列名前缀

autoCreateTime => 创建时追踪当前时间,对于 int 字段,它会追踪秒级时间戳,您可以使用 nano/milli 来追踪纳秒、毫秒时间戳,例如:autoCreateTime:nano

index => 根据参数创建索引,多个字段使用相同的名称则创建复合索引,查看 索引 获取详情

<- => 设置字段写入的权限, <-:create 只创建、<-:update 只更新、<-:false 无写入权限、<- 创建和更新权限

-> => 设置字段读的权限,->:false 无读权限

  •  =>      忽略该字段,- 无读写权限
    

comment => 迁移时为字段添加注释

//举个栗子

type User struct {
  gorm.Model
  Name         string
  Age          sql.NullInt64
  Birthday     *time.Time
  Email        string  `gorm:"type:varchar(100);unique_index"`
  Role         string  `gorm:"size:255"` // 设置字段大小为255
  MemberNumber *string `gorm:"unique;not null"` // 设置会员号(member number)唯一并且不为空
  Num          int     `gorm:"AUTO_INCREMENT"` // 设置 num 为自增类型
  Address      string  `gorm:"index:addr"` // 给address字段创建名为addr的索引
  IgnoreMe     int     `gorm:"-"` // 忽略本字段
}

4、创建记录

按照之前的开发经验,数据库记录的创建无非实现方式是单一插入和批量插入,但是Gorm中实现的方式好像比较多,使用上根据数据结构的不同也有所差异,接下来针对这些创建记录的方式展开讲解

//通过数据的指针来创建

user := User{Name: "Jinzhu", Age: 18, Birthday: time.Now()}
result := db.Create(&user) // 通过数据的指针来创建
user.ID             // 返回插入数据的主键
result.Error        // 返回 error
result.RowsAffected // 返回插入记录的条数

//使用指定的字段来创建

// INSERT INTO `users` (`name`,`age`,`created_at`) VALUES ("jinzhu", 18, "2020-07-04 11:05:21.775")
db.Select("Name", "Age", "CreatedAt").Create(&user) 

//忽略给出的字段来创建

// INSERT INTO `users` (`birthday`,`updated_at`) VALUES ("2020-01-01 00:00:00.000", "2020-07-04 11:05:21.775")
db.Omit("Name", "Age", "CreatedAt").Create(&user)

//批量插入,传递切片给Create方法并回填创建的主键的值

var users = []User{{Name: "jinzhu1"}, {Name: "jinzhu2"}, {Name: "jinzhu3"}}
db.Create(&users)
for _, user := range users {
  user.ID // 1,2,3
}

//批量插入,并指定插入的记录的数量

var users = []User{{name: "jinzhu_1"}, ...., {Name: "jinzhu_10000"}}
db.CreateInBatches(users, 100)

//数据结构为Map时进行创建,主要分为两种格式:map[string]interface{} 和 []map[string]interface{}{}
db.Model(&User{}).Create(map[string]interface{}{
  "Name": "jinzhu", "Age": 18,
})

db.Model(&User{}).Create([]map[string]interface{}{
  {"Name": "jinzhu_1", "Age": 18},
  {"Name": "jinzhu_2", "Age": 20},
})

5、查询记录

查询功能其实相对于sql的其他功能而言频率是非常的高的,因此这部分的内容也是非常的重要,由于查询的语句变化非常的多,对应的Gorm中变化也是非常的多,简单而言如果把查询掌握好了,基本上这个框架也算是掌握了,针对查询可以总结为以下的几类:简单查询、条件查询、子查询、连接查询.

检索单个对象:First、Take、Last应用

//获取第一条记录
//SELECT * FROM users ORDER BY id LIMIT 1;
//db.First(&user)

//获取一条记录,没有指定排序字段
//SELECT * FROM users LIMIT 1;
//db.Take(&user)

//获取最后一条记录(主键降序)
//SELECT * FROM users ORDER BY id DESC LIMIT 1;
//db.Last(&user)


//附属的属性
result := db.First(&user)
result.RowsAffected // 返回找到的记录数
result.Error        // returns error

// 检查 ErrRecordNotFound 错误
errors.Is(result.Error, gorm.ErrRecordNotFound

使用主键进行检索

//SELECT * FROM users WHERE id = 10;
db.First(&user, 10)

// SELECT * FROM users WHERE id = 10;
db.First(&user, "10")

//SELECT * FROM users WHERE id IN (1,2,3);
db.Find(&users, []int{1,2,3})

//如果主键是像 uuid 这样的字符串,您需要这要写:
// SELECT * FROM users WHERE id = "1b74413f-f3b8-409f-ac47-e8c062e3472a";
db.First(&user, "id = ?", "1b74413f-f3b8-409f-ac47-e8c062e3472a")

检索全部的对象

// 获取全部记录
result := db.Find(&users)
// SELECT * FROM users;

result.RowsAffected // 返回找到的记录数,相当于 `len(users)`
result.Error        // returns error

string条件

// 获取第一条匹配的记录
// SELECT * FROM users WHERE name = 'jinzhu' ORDER BY id LIMIT 1;
db.Where("name = ?", "jinzhu").First(&user)

// 获取全部匹配的记录
// SELECT * FROM users WHERE name <> 'jinzhu';
db.Where("name <> ?", "jinzhu").Find(&users)

// IN
// SELECT * FROM users WHERE name IN ('jinzhu','jinzhu 2');
db.Where("name IN ?", []string{"jinzhu", "jinzhu 2"}).Find(&users)

// LIKE
// SELECT * FROM users WHERE name LIKE '%jin%';
db.Where("name LIKE ?", "%jin%").Find(&users)

// AND
// SELECT * FROM users WHERE name = 'jinzhu' AND age >= 22;
db.Where("name = ? AND age >= ?", "jinzhu", "22").Find(&users)

// Time
// SELECT * FROM users WHERE updated_at > '2000-01-01 00:00:00';
db.Where("updated_at > ?", lastWeek).Find(&users)

// BETWEEN
// SELECT * FROM users WHERE created_at BETWEEN '2000-01-01 00:00:00' AND '2000-01-08 00:00:00';
db.Where("created_at BETWEEN ? AND ?", lastWeek, today).Find(&users)

Struct和Map条件

// Struct
// SELECT * FROM users WHERE name = "jinzhu" AND age = 20 ORDER BY id LIMIT 1;
db.Where(&User{Name: "jinzhu", Age: 20}).First(&user)

// Map
// SELECT * FROM users WHERE name = "jinzhu" AND age = 20;
db.Where(map[string]interface{}{"name": "jinzhu", "age": 20}).Find(&users)

// 主键切片条件
// SELECT * FROM users WHERE id IN (20, 21, 22);
db.Where([]int64{20, 21, 22}).Find(&users)

指定结构体查询字段

// SELECT * FROM users WHERE name = "jinzhu" AND age = 0;
db.Where(&User{Name: "jinzhu"}, "name", "Age").Find(&users)

// SELECT * FROM users WHERE age = 0;
db.Where(&User{Name: "jinzhu"}, "Age").Find(&users)

内联条件

// SELECT * FROM users WHERE id = 23;
// 根据主键获取记录,如果是非整型主键
db.First(&user, "id = ?", "string_primary_key")
// SELECT * FROM users WHERE id = 'string_primary_key';

// Plain SQL
// SELECT * FROM users WHERE name = "jinzhu";
db.Find(&user, "name = ?", "jinzhu")

// SELECT * FROM users WHERE name <> "jinzhu" AND age > 20;
db.Find(&users, "name <> ? AND age > ?", "jinzhu", 20)

// Struct
// SELECT * FROM users WHERE age = 20;
db.Find(&users, User{Age: 20})

// Map
// SELECT * FROM users WHERE age = 20;
db.Find(&users, map[string]interface{}{"age": 20})

Not条件

// SELECT * FROM users WHERE NOT name = "jinzhu" ORDER BY id LIMIT 1;
db.Not("name = ?", "jinzhu").First(&user)

// Not In
// SELECT * FROM users WHERE name NOT IN ("jinzhu", "jinzhu 2");
db.Not(map[string]interface{}{"name": []string{"jinzhu", "jinzhu 2"}}).Find(&users)

// Struct
// SELECT * FROM users WHERE name <> "jinzhu" AND age <> 18 ORDER BY id LIMIT 1;
db.Not(User{Name: "jinzhu", Age: 18}).First(&user)

// 不在主键切片中的记录
// SELECT * FROM users WHERE id NOT IN (1,2,3) ORDER BY id LIMIT 1;
db.Not([]int64{1,2,3}).First(&user)

Or条件

// SELECT * FROM users WHERE role = 'admin' OR role = 'super_admin';
db.Where("role = ?", "admin").Or("role = ?", "super_admin").Find(&users)

// Struct
// SELECT * FROM users WHERE name = 'jinzhu' OR (name = 'jinzhu 2' AND age = 18);
db.Where("name = 'jinzhu'").Or(User{Name: "jinzhu 2", Age: 18}).Find(&users)

// Map
// SELECT * FROM users WHERE name = 'jinzhu' OR (name = 'jinzhu 2' AND age = 18);
db.Where("name = 'jinzhu'").Or(map[string]interface{}{"name": "jinzhu 2", "age": 18}).Find(&users)

选择特定字段

// SELECT name, age FROM users;
db.Select("name", "age").Find(&users)

// SELECT name, age FROM users;
db.Select([]string{"name", "age"}).Find(&users)

// SELECT COALESCE(age,'42') FROM users;
db.Table("users").Select("COALESCE(age,?)", 42).Rows()

Order条件

// SELECT * FROM users ORDER BY age desc, name;
db.Order("age desc, name").Find(&users)

// 多个 order
// SELECT * FROM users ORDER BY age desc, name;
db.Order("age desc").Order("name").Find(&users)

// SELECT * FROM users ORDER BY FIELD(id,1,2,3)
db.Clauses(clause.OrderBy{
  Expression: clause.Expr{SQL: "FIELD(id,?)", Vars: []interface{}{[]int{1, 2, 3}}, WithoutParentheses: true},
}).Find(&User{})

Limit和Offset

// SELECT * FROM users LIMIT 3;
db.Limit(3).Find(&users)

// 通过 -1 消除 Limit 条件
// SELECT * FROM users LIMIT 10; (users1)
// SELECT * FROM users; (users2)
db.Limit(10).Find(&users1).Limit(-1).Find(&users2)

// SELECT * FROM users OFFSET 3;
db.Offset(3).Find(&users)

// SELECT * FROM users OFFSET 5 LIMIT 10;
db.Limit(10).Offset(5).Find(&users)

// 通过 -1 消除 Offset 条件
// SELECT * FROM users OFFSET 10; (users1)
// SELECT * FROM users; (users2)
db.Offset(10).Find(&users1).Offset(-1).Find(&users2)

Group和Having

type result struct {
  Date  time.Time
  Total int
}

// SELECT name, sum(age) as total FROM `users` WHERE name LIKE "group%" GROUP BY `name`
db.Model(&User{}).Select("name, sum(age) as total").Where("name LIKE ?", "group%").Group("name").First(&result)

// SELECT name, sum(age) as total FROM `users` GROUP BY `name` HAVING name = "group"
db.Model(&User{}).Select("name, sum(age) as total").Group("name").Having("name = ?", "group").Find(&result)

rows, err := db.Table("orders").Select("date(created_at) as date, sum(amount) as total").
Group("date(created_at)").Rows()
for rows.Next() {
  ...
}

rows, err := db.Table("orders").Select("date(created_at) as date, sum(amount) as total").
Group("date(created_at)").Having("sum(amount) > ?", 100).Rows()
for rows.Next() {
  ...
}

type Result struct {
  Date  time.Time
  Total int64
}
db.Table("orders").Select("date(created_at) as date, sum(amount) as total").Group("date(created_at)").
Having("sum(amount) > ?

Distinct用法

db.Distinct("name", "age").Order("name, age desc").Find(&results)

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{})
// SELECT users.name, emails.email FROM `users` left join emails on emails.user_id = users.id

rows, err := db.Table("users").Select("users.name, emails.email").Joins("left join emails on emails.user_id = users.id").Rows()
for rows.Next() {
  ...
}

db.Table("users").Select("users.name, emails.email").Joins("left join emails on emails.user_id = users.id").Scan(&results)

// 带参数的多表连接
db.Joins("JOIN emails ON emails.user_id = users.id AND emails.email = ?", "[email protected]").
Joins("JOIN credit_cards ON credit_cards.user_id = users.id").Where("credit_cards.number = ?", "
411111111111").Find(&user)

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)

6、查询高级用法

智能选择字段

type User struct {
  ID     uint
  Name   string
  Age    int
  Gender string
  // 假设后面还有几百个字段...
}

type APIUser struct {
  ID   uint
  Name string
}

// 查询时会自动选择 `id`, `name` 字段
// SELECT `id`, `name` FROM `users` LIMIT 10
db.Model(&User{}).Limit(10).Find(&APIUser{})

子查询

// SELECT * FROM "orders" WHERE amount > (SELECT AVG(amount) FROM "orders");
db.Where("amount > (?)", db.Table("orders").Select("AVG(amount)")).Find(&orders)

// SELECT AVG(age) as avgage FROM `users` GROUP BY `name` HAVING AVG(age) > (SELECT AVG(age) FROM `users` WHERE name LIKE "name%")
subQuery := db.Select("AVG(age)").Where("name LIKE ?", "name%").Table("users")
db.Select("AVG(age) as avgage").Group("name").Having("AVG(age) > (?)", subQuery).Find(&results)

From子查询

// SELECT * FROM (SELECT `name`,`age` FROM `users`) as u WHERE `age` = 18
db.Table("(?) as u", db.Model(&User{}).Select("name", "age")).Where("age = ?", 18}).Find(&User{})

// SELECT * FROM (SELECT `name` FROM `users`) as u, (SELECT `name` FROM `pets`) as p
subQuery1 := db.Model(&User{}).Select("name")
subQuery2 := db.Model(&Pet{}).Select("name")
db.Table("(?) as u, (?) as p", subQuery1, subQuery2).Find(&User{})

Find到Map

var result map[string]interface{}
db.Model(&User{}).First(&result, "id = ?", 1)

var results []map[string]interface{}
db.Table("users").Find(&results)

count关键字

// SELECT count(1) FROM users WHERE name = 'jinzhu' OR name = 'jinzhu 2'
var count int64
db.Model(&User{}).Where("name = ?", "jinzhu").Or("name = ?", "jinzhu 2").Count(&count)

// SELECT count(1) FROM users WHERE name = 'jinzhu'; (count)
db.Model(&User{}).Where("name = ?", "jinzhu").Count(&count)

// SELECT count(1) FROM deleted_users;
db.Table("deleted_users").Count(&count)

// Count with Distinct
// SELECT COUNT(DISTINCT(`name`)) FROM `users`
db.Model(&User{}).Distinct("name").Count(&count)

// SELECT count(distinct(name)) FROM deleted_users

db.Table("deleted_users").Select("count(distinct(name))").Count(&count)
// SELECT count(distinct(name)) FROM deleted_users

// Count with Group
users := []User{
  {Name: "name1"},
  {Name: "name2"},
  {Name: "name3"},
  {Name: "name3"},
}


//output:3
db.Model(&User{}).Group("name").Count(&count)

7、更新记录

保存所有的字段,即使字段是零值


// UPDATE users SET name='jinzhu 2', age=100, birthday='2016-01-01', updated_at = '2013-11-17 21:34:10' WHERE id=111;
db.First(&user)
user.Name = "jinzhu 2"
user.Age = 100
db.Save(&user)

更新单个列

// 条件更新
// UPDATE users SET name='hello', updated_at='2013-11-17 21:34:10' WHERE active=true;
db.Model(&User{}).Where("active = ?", true).Update("name", "hello")

// User 的 ID 是 `111`
// UPDATE users SET name='hello', updated_at='2013-11-17 21:34:10' WHERE id=111;
db.Model(&user).Update("name", "hello")

// 根据条件和 model 的值进行更新
// UPDATE users SET name='hello', updated_at='2013-11-17 21:34:10' WHERE id=111 AND active=true;
db.Model(&user).Where("active = ?", true).Update("name", "hello")

更新多列

// 根据 `struct` 更新属性,只会更新非零值的字段
// UPDATE users SET name='hello', age=18, updated_at = '2013-11-17 21:34:10' WHERE id = 111;
db.Model(&user).Updates(User{Name: "hello", Age: 18, Active: false})

// 根据 `map` 更新属性
// UPDATE users SET name='hello', age=18, active=false, updated_at='2013-11-17 21:34:10' WHERE id=111;
db.Model(&user).Updates(map[string]interface{}{"name": "hello", "age": 18, "active": false})

更新选定的字段

// 使用 Map 进行 Select
// User's ID is `111`:
// UPDATE users SET name='hello' WHERE id=111;
db.Model(&user).Select("name").Updates(map[string]interface{}{"name": "hello", "age": 18, "active": false})

// UPDATE users SET age=18, active=false, updated_at='2013-11-17 21:34:10' WHERE id=111;
db.Model(&user).Omit("name").Updates(map[string]interface{}{"name": "hello", "age": 18, "active": false})

// 使用 Struct 进行 Select(会 select 零值的字段)
// UPDATE users SET name='new_name', age=0 WHERE id=111;
db.Model(&user).Select("Name", "Age").Updates(User{Name: "new_name", Age: 0})

// Select 所有字段(查询包括零值字段的所有字段)
db.Model(&user).Select("*").Update(User{Name: "jinzhu", Role: "admin", Age: 0})

// Select 除 Role 外的所有字段(包括零值字段的所有字段)
db.Model(&user).Select("*").Omit("Role").Update(User{Name: "jinzhu", Role: "admin", Age: 0})

批量更新

// 根据 struct 更新
// UPDATE users SET name='hello', age=18 WHERE role = 'admin;
db.Model(User{}).Where("role = ?", "admin").Updates(User{Name: "hello", Age: 18})

// 根据 map 更新
// UPDATE users SET name='hello', age=18 WHERE id IN (10, 11);
db.Table("users").Where("id IN ?", []int{10, 11}).Updates(map[string]interface{}{"name": "hello", "age": 18})

阻止全局更新

db.Model(&User{}).Update("name", "jinzhu").Error // gorm.ErrMissingWhereClause

// UPDATE users SET `name` = "jinzhu" WHERE 1=1
db.Model(&User{}).Where("1 = 1").Update("name", "jinzhu")

// UPDATE users SET name = "jinzhu"
db.Exec("UPDATE users SET name = ?", "jinzhu")

// UPDATE users SET `name` = "jinzhu"
db.Session(&gorm.Session{AllowGlobalUpdate: true}).Model(&User{}).Update("name", "jinzhu")

使用SQL表达式更新

// product 的 ID 是 `3`
// UPDATE "products" SET "price" = price * 2 + 100, "updated_at" = '2013-11-17 21:34:10' WHERE "id" = 3;
db.Model(&product).Update("price", gorm.Expr("price * ? + ?", 2, 100))

// UPDATE "products" SET "price" = price * 2 + 100, "updated_at" = '2013-11-17 21:34:10' WHERE "id" = 3;
db.Model(&product).Updates(map[string]interface{}{"price": gorm.Expr("price * ? + ?", 2, 100)})

// UPDATE "products" SET "quantity" = quantity - 1 WHERE "id" = 3;
db.Model(&product).UpdateColumn("quantity", gorm.Expr("quantity - ?", 1))

// UPDATE "products" SET "quantity" = quantity - 1 WHERE "id" = 3 AND quantity > 1;
db.Model(&product).Where("quantity > 1").UpdateColumn("quantity", gorm.Expr("quantity - ?", 1))

根据子查询更新

db.Model(&user).Update("company_name", db.Model(&Company{}).Select("name").Where("companies.id = users.company_id"))
// UPDATE "users" SET "company_name" = (SELECT name FROM companies WHERE companies.id = users.company_id);

db.Table("users as u").Where("name = ?", "jinzhu").Update("company_name", db.Table("companies as c").Select("name").Where("c.id = u.company_id"))

db.Table("users as u").Where("name = ?", "jinzhu").Updates(map[string]interface{}{}{"company_name": db.Table("companies as c").Select("name").Where("c.id = u.company_id")})

8、删除记录

删除单条记录


// Email 的 ID 是 `10`
// DELETE from emails where id = 10;
db.Delete(&email)

// 带额外条件的删除
// DELETE from emails where id = 10 AND name = "jinzhu";
db.Where("name = ?", "jinzhu").Delete(&email)

根据主键删除

// DELETE FROM users WHERE id = 10;
db.Delete(&User{}, 10)


// DELETE FROM users WHERE id = 10;
db.Delete(&User{}, "10")

// DELETE FROM users WHERE id IN (1,2,3);
db.Delete(&users, []int{1,2,3})

批量删除

// DELETE from emails where email LIKE "%jinzhu%";
db.Where("email LIKE ?", "%jinzhu%").Delete(Email{})

// DELETE from emails where email LIKE "%jinzhu%";
db.Delete(Email{}, "email LIKE ?", "%jinzhu%")

阻止全局删除

db.Delete(&User{}).Error // gorm.ErrMissingWhereClause

// DELETE FROM `users` WHERE 1=1
db.Where("1 = 1").Delete(&User{})

// DELETE FROM users
db.Exec("DELETE FROM users")

// DELETE FROM users
db.Session(&gorm.Session{AllowGlobalUpdate: true}).Delete(&User{})

软删除

如果您的模型包含了一个 gorm.DeletedAt 字段(gorm.Model 已经包含了该字段),它将自动获得软删除的能力!拥有软删除能力的模型调用 Delete 时,记录不会被从数据库中真正删除。但 GORM 会将 DeletedAt 置为当前时间, 并且你不能再通过正常的查询方法找到该记录。

// user 的 ID 是 `111`
// UPDATE users SET deleted_at="2013-10-29 10:23" WHERE id = 111;
db.Delete(&user)

// 批量删除
// UPDATE users SET deleted_at="2013-10-29 10:23" WHERE age = 20;
db.Where("age = ?", 20).Delete(&User{})

// 在查询时会忽略被软删除的记录
// SELECT * FROM users WHERE age = 20 AND deleted_at IS NULL;
db.Where("age = 20").Find(&user)


//如果您不想引入 gorm.Model,您也可以这样启用软删除特性
type User struct {
  ID      int
  Deleted gorm.DeletedAt
  Name    string
}

//查找被软删除的记录
// SELECT * FROM users WHERE age = 20;
db.Unscoped().Where("age = 20").Find(&users)

//永久删除
// DELETE FROM orders WHERE id=10;
db.Unscoped().Delete(&order)

Hook

Hook中文意思是钩子,是在创建、查询、更新、删除等操作之前、之后调用的函数,作用类似与MySQL中的触发器.

//创建之前和之后触发
func (u *User) BeforeCreate(tx *gorm.DB) (err error) {
  u.UUID = uuid.New()

  if !u.IsValid() {
    err = errors.New("can't save invalid data")
  }
  return
}

func (u *User) AfterCreate(tx *gorm.DB) (err error) {
  if u.ID == 1 {
    tx.Model(u).Update("role", "admin")
  }
  return
}

//更新之前和更新之后触发
func (u *User) BeforeUpdate(tx *gorm.DB) (err error) {
  if u.readonly() {
    err = errors.New("read only user")
  }
  return
}

// 在同一个事务中更新数据
func (u *User) AfterUpdate(tx *gorm.DB) (err error) {
  if u.Confirmed {
    tx.Model(&Address{}).Where("user_id = ?", u.ID).Update("verfied", true)
  }
  return
}

//删除之前和删除之后触发

// 在同一个事务中更新数据

func (u *User) BeforeDelete(tx *gorm.DB) (err error) {
  if u.Confirmed {
    tx.Model(&Address{}).Where("user_id = ?", u.ID).Update("invalid", false)
  }
  return
}

func (u *User) AfterDelete(tx *gorm.DB) (err error) {
  if u.Confirmed {
    tx.Model(&Address{}).Where("user_id = ?", u.ID).Update("invalid", false)
  }
  return
}

小结

Gorm相关的内容大概总结的内容就是这些,主要都是从一些实际操作的内容出发来讲解的,掌握了以上这些内容,基本的业务开发应该就没有多大的问题了。

你可能感兴趣的:(go,gorm)