CRUD接口
1、创建
1、创建记录
user := User{Name: "zhihui", Age: 18, Birthday: time.Now()}
db.NewRecord(user) // 返回true,因为主键为空
db.Create(&user)
db.NewRecord(user) // 在‘user’之后创建返回false
2、默认值
可以通过标签定义字段的默认值,如下所示:
type Animal struct {
ID int64
Name string `gorm:"default: 'galeone'"`
Age int64
}
通过上述步骤,SQL会排除没有值或零值的字段,在记录插入数据库之后,gorm将从数据库加载这些字段的值
var animal = Animal{Age: 99, Name: ""} // 像0、‘’、false或者其他零值是不会被保存到数据库中,但会使用这个字段的默认值 => 考虑使用指针类型或其他的值来避免这种情况
db.Create(&animal)
// Insert into animals("age") value('99')
// select name from animals where ID = 111; // 返回的主键是111
// animal.Name => ‘galeone’
3、在钩子中设置字段的值
如果想在BeforeCreate函数中更新字段的值,应该使用scope.SetColumn,如下所示:
func (user *User) BeforeCreate(scope *gorm.Scope) error {
scope.SetColumn("ID", uuid.New())
return nil
}
4、创建额外选项
// 为插入SQL语句添加额外选项
db.Set("gorm:insert_option", "ON CONFLICT").Create(&product)
// 等同于 : insert into products (name, code) values ("name", "code") on conflict
2、查询
1、整体大纲
名称 | 说明 |
---|---|
简单查询 | 不带条件的查询如第一条、最后一条以及所有记录 |
Where | 条件查询 |
Not | 除满足条件外其他记录 |
Or | 或者的意思,多个条件满足一个即可 |
行内查询条件 | 不使用结构的链式法则,类似写SQL语句,?充当占位符 |
额外查询选项 | 请看2.6小节实例 |
FirstOrInit | 匹配第一条记录,否则初始化这个记录(适用场景:struct 和 map) |
Attrs | 未找到记录,使用参数初始化(经常与FirstOrInit一起使用) |
Assign | 无论是否查询到记录,都将参数赋值给struct |
FirstOrCreate | 匹配第一条记录,否则通过给定的条件创建一条记录(适用场景:struct 和 map) |
2.1、简单查询
// 获取第一条记录,按主键排序
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.Find(&users) // = select * from users;
// 通过主键进行查询(仅适用于主键是数字类型)
db.First(&user, 10) // = select * from users where id = 10;
2.2、Where条件查询
1、原生SQL
学过Mysql的同学应该不会对这些例子太陌生,就是写法需要注意适应一下。
// 获取第一条匹配的记录
db.Where("name = ?", "jinzhu").First(&user) // SELECT * FROM users WHERE name = 'jinzhu' limit 1;
// 获取所有匹配的记录
db.Where("name = ?", "jinzhu").Find(&users) // SELECT * FROM users WHERE name = 'jinzhu';
// <>
db.Where("name <> ?", "jinzhu").Find(&users)
// IN
db.Where("name in (?)", []string{"jinzhu", "jinzhu 2"}).Find(&users)
// LIKE
db.Where("name LIKE ?", "%jin%").Find(&users)
// AND
db.Where("name = ? AND age >= ?", "jinzhu", "22").Find(&users)
// Time
db.Where("updated_at > ?", lastWeek).Find(&users)
// BETWEEN
db.Where("created_at BETWEEN ? AND ?", lastWeek, today).Find(&users)
2、Struct & Map
// Struct
db.Where(&User{Name: "jinzhu", Age: 20}).First(&user) // SELECT * FROM users WHERE name = "jinzhu" AND age = 20 LIMIT 1;
// Map
db.Where(map[string]interface{}{"name": "jinzhu", "age": 20}).Find(&users) // SELECT * FROM users WHERE name = "jinzhu" AND age = 20;
// 多主键 slice 查询
db.Where([]int64{20, 21, 22}).Find(&users) // SELECT * FROM users WHERE id IN (20, 21, 22);
这里有值得注意
的地方:通过strcut进行查询的时候,gorm将会查询这些字段的非零值,意味着你的字段包含0、‘’、false或其他零值的不会出现在查询语句中,如:
db.Where(&User{Name: "chenzhihui", Age: 0}).Find(&users) // = select * from users where name = "chenzhihui"
// 上述的例子可以看到,Age = 0并没有出现在查询语句中,但是这样是存在问题的,比如在商品查询场景,我们要查询哪些商品没有库存,这个时候确没有将这个重要的条件纳入!
指针类型 or scanner/valuer来避免这种情况!!!如下👇
// 指针类型
type User struct {
gorm.Model
Name string
Age *int
}
// 使用scanner/valuer
type User struct {
gorm.Model
Name string
Age sql.NullInt64
}
2.3、Not
Not的话是和Where查询类似。如下👇
db.Not("name", "zhihui").First(&user)
SELECT * FROM users WHERE name <> "zhihui" LIMIT 1;
// 不包含
db.Not("name", []string{"zhihui", "zhihui 2"}).Find(&users)
SELECT * FROM users WHERE name NOT IN ("zhihui", "zhihui 2");
//不在主键 slice 中
db.Not([]int64{1,2,3}).First(&user)
SELECT * FROM users WHERE id NOT IN (1,2,3);
db.Not([]int64{}).First(&user)
SELECT * FROM users;
// 原生 SQL
db.Not("name = ?", "zhihui").First(&user)
SELECT * FROM users WHERE NOT(name = "zhihui");
// Struct
db.Not(User{Name: "zhihui"}).First(&user)
SELECT * FROM users WHERE name <> "zhihui";
2.4、Or
db.Where("role = ?", "admin").Or("role = ?", "super_admin").Find(&users)
SELECT * FROM users WHERE role = 'admin' OR role = 'super_admin';
// Struct
db.Where("name = 'zhihui'").Or(User{Name: "zhihui 2"}).Find(&users)
SELECT * FROM users WHERE name = 'zhihui' OR name = 'zhihui 2';
// Map
db.Where("name = 'zhihui'").Or(map[string]interface{}{"name": "zhihui 2"}).Find(&users)
SELECT * FROM users WHERE name = 'zhihui' OR name = 'zhihui 2';
2.5、行内查询条件
同样的,行内查询也和Where类似,需要注意的是,当使用链式调用传入行内条件查询的时候,这些查询不会被传参给后续的中间方法。如下👇
// 通过主键进行查询 (仅适用于主键是数字类型)
db.First(&user, 23)
SELECT * FROM users WHERE id = 23 LIMIT 1;
// 非数字类型的主键查询
db.First(&user, "id = ?", "string_primary_key")
SELECT * FROM users WHERE id = 'string_primary_key' LIMIT 1;
// 原生 SQL
db.Find(&user, "name = ?", "zhihui")
SELECT * FROM users WHERE name = "zhihui";
db.Find(&users, "name <> ? AND age > ?", "zhihui", 20)
SELECT * FROM users WHERE name <> "zhihui" AND age > 20;
// Struct
db.Find(&users, User{Age: 20})
SELECT * FROM users WHERE age = 20;
// Map
db.Find(&users, map[string]interface{}{"age": 20})
SELECT * FROM users WHERE age = 20;
2.6、额外的查询选项
// 为查询SQL添加额外的选项
db.Set("gorm: query_option", "FOR UPDATE").First(&user, 10) // = select * from users where id = 10 for update;
// 这条 SQL 查询语句用于锁定 id 为 10 的记录,以防止其他事务对该记录进行更改。具体来说,FOR UPDATE 是一个行级锁定的操作,它会锁定查询到的所有行,以防止其他事务修改这些行。在这个例子中,只有一个 id 为 10 的记录会被锁定。其他查询同样会锁定查询到的行。这种锁定是基于数据库事务的概念,需要使用事务来执行查询语句。在事务结束前,锁定的行将一直被占用,其他事务无法修改这些行。
2.7、FirstOrInit
获取第一条匹配的记录,或者通过给定的条件初始化一条新的记录(仅适用于struct 和 map条件)
// 未查询到
db.FirstOrInit(&user, User{Name: "non_existing"})
user -> User{Name: "non_existing"}
// 查询到
db.Where(User{Name: "Jinzhu"}).FirstOrInit(&user)
user -> User{Id: 111, Name: "Jinzhu", Age: 20}
db.FirstOrInit(&user, map[string]interface{}{"name": "jinzhu"})
user -> User{Id: 111, Name: "Jinzhu", Age: 20}
2.8、Attrs
如果未找到记录,则使用参数初始化struct => 可以看得出通常跟FirstOrInit一起使用
// 未查询到
db.Where(User{Name: "non_existing"}).Attrs(User{Age: 20}).FirstOrInit(&user)
SELECT * FROM USERS WHERE name = 'non_existing';
user -> User{Name: "non_existing", Age: 20}
db.Where(User{Name: "non_existing"}).Attrs("age", 20).FirstOrInit(&user)
SELECT * FROM USERS WHERE name = 'non_existing';
user -> User{Name: "non_existing", Age: 20}
// 查询到
db.Where(User{Name: "Jinzhu"}).Attrs(User{Age: 30}).FirstOrInit(&user)
SELECT * FROM USERS WHERE name = jinzhu';
user -> User{Id: 111, Name: "Jinzhu", Age: 20}
2.9、Assign
无论是否查询到数据,都将参数赋值给struct
// 未查询到
db.Where(User{Name: "no_existing"}).Assign(User{Age: 20}).FirstOrInit(&user)
// user -> User{Name: "no_existing", Age: 20}
// 查询到
db.Where(User{Name: "zhihui"}).Assign(User{Age: 30}).FirstOrInit(&user)
// select * from users where name = "zhihui"
// user -> User{Id: 111, Name: zhihui, Age: 30}
2.10、FirstOrCreate
获取第一条匹配的记录,或者通过给定的条件创建一条记录(仅适用于struct和map条件)
// 未查询到
db.FirstOrCreate(&user, User{Name: "no_exsting"})
// insert into "users" (name) values ("no_existing");
// 查询到
db.Where(User{Name: "zhihui"}).FirstOrCreate(&user)
// user -> User{ID: 111, Name: "zhihui"}
3、高级查询
1、大纲汇总
名称 | 说明 |
---|---|
gorm.expr 子查询 | 主要用在主查询中,主查询的条件,使用子查询的结果 |
指定查询结果字段个数 | 参照3.2小节 |
Order排序条件 | 通常有desc降序、asc升序 |
Limit指定查询最大记录数 | 如limit 1 返回一条 |
Offset 跳过指定记录数 | 跳过指定的记录数 |
Count 获取模型记录条数 | 通常用来统计个数 |
Group & Having 分组查询 | 对需要的查询条件进行分组 |
关联条件 | 详细查看3.8小节 |
Pluck | 从数据库中检索指定列的值,并将其填充到切片中 |
Scan | 类似Pluck,不过Scan是可以同时制定多列的值,并填充到结构中 |
3.1、子查询
子查询通常使用“gorm.expr”进行子查询,具体例子如下👇
db.Where("amount > ?", DB.Table("orders").Select("AVG(amount)").Where("state = ?", "paid").QueryExpr()).Find(&orders)
// 这个查询语句的含义是:查询 orders 表中所有 amount 字段值大于另一个子查询的结果的记录,这个子查询使用了聚合函数 AVG 计算 amount 字段中 state 字段为 "paid" 的所有记录的平均值,并将结果作为比较条件。
// 具体来说,DB.Table("orders").Select("AVG(amount)").Where("state = ?", "paid").QueryExpr() 这部分是子查询,用于查询 state 字段为 "paid" 的所有记录的 amount 字段平均值,返回的是一个 *gorm.DB 对象,然后将这个对象的 QueryExpr() 方法返回的 SQL 表达式作为参数传递给了 Where 方法,实现了在主查询中使用子查询的功能。
3.2、指定查询结果字段个数
从数据库中检索符合条件的字段,默认情况下,将选择所有字段,下面举例子,如何指定我们需要的字段👇
// 格式1
db.Select("name", "age").Find(&user) // select name, age from users;
// 格式2
db.Select([]string{"name", "age"}).Find(&users) // select name, age from users;
// 格式3
db.Table("users").Select("coalesce(age, ?)", 42).Rows(); // select coalesce(age, '42') from users;
// 解释上面这个部分
// 这行代码使用了 GORM 中的表达式方法 Table 和 Select,表示使用 users 表,并选取 age 列的值,如果该列的值为 NULL,则将其替换为 42,最后调用 Rows 方法返回查询结果的行迭代器。
// coalesce 是一个 SQL 函数,可以用于处理空值,它接受任意个参数,返回第一个非空值。在这个例子中,我们将 age 和 42 作为 coalesce 的两个参数,如果 age 列的值为 NULL,则会返回 42,否则返回 age 的值。
3.3、Order 排序条件
通常使用order从数据库查询记录时候,当第二个参数设置为true时,将会覆盖之前到定义条件。
db.Order("age desc, name").Find(&users)
// SELECT * FROM users ORDER BY age desc, name;
// 多个排序条件
db.Order("age desc").Order("name").Find(&users)
// SELECT * FROM users ORDER BY age desc, name;
// 重新排序
db.Order("age desc").Find(&users1).Order("age", true).Find(&users2)
// SELECT * FROM users ORDER BY age desc; (users1)
// SELECT * FROM users ORDER BY age; (users2)
3.4、Limit 指定查询最大记录数
limit的作用通常是用来限制当前查询结果的返回次数。
db.Limit(3).Find(&users)
// select * from users limit 3;
// 用-1取消limit限制条件
db.Limit(10).Find(&users1).Limit(-1).Find(&users2)
// select * from users limit 10;(users1)
// select * from users;(users2)
3.5、Offset 跳过指定记录数
指定在开始之前,返回记录时,想要跳过的记录数。
db.Offset(3).Find(&users)
// select * from users offset 3;
// 用-1取消offset限制条件,同上面limit,就不在加以说明
3.6、Count 获取模型记录条数
count函数主要是用来统计查询个数。
db.Where("name = ?", "zhihui").Or("name = ?", "zhihui 2").Find(&users).Count(&count)
// SELECT * from USERS WHERE name = 'zhihui' OR name = 'zhihui 2'; (users)
// SELECT count(*) FROM users WHERE name = 'zhihui' OR name = 'zhihui 2'; (count)
db.Model(&User{}).Where("name = ?", "zhihui").Count(&count)
// SELECT count(*) FROM users WHERE name = 'zhihui'; (count)
db.Table("deleted_users").Count(&count)
// SELECT count(*) FROM deleted_users;
查询链中使用count的时候,必须放在最后一个位置,因为它会覆盖select查询条件!!!
3.7、Group & Having 分组查询
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) > ?", 100).Scan(&results)
// 这段代码是在使用 GORM 进行数据库查询操作,主要是查询订单表 orders 中按日期分组的订单总额超过 100 的结果。
第一段代码使用了 Rows() 方法获取结果集的游标,并使用 Next() 方法逐行读取结果集数据。需要注意的是,需要在 rows.Close() 之后才会释放数据库连接。如果在 Next() 循环过程中出现了错误,则需要使用 rows.Err() 来获取错误信息。
第二段代码在第一段的基础上添加了 Having() 方法,用于进一步筛选订单总额大于 100 的分组数据。
第三段代码则是直接使用 Scan() 方法将结果映射到结构体中,其中定义了一个名为 Result 的结构体,用于存储日期和订单总额的信息。需要注意的是,结构体字段名应与查询语句中的别名对应。
3.8、关联条件
指定关联条件,如下所示👇
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 = ?", "zhihui@example.org").Joins("JOIN credit_cards ON credit_cards.user_id = users.id").Where("credit_cards.number = ?", "111111").Find(&user)
// 这些代码演示了如何在 GORM 中进行关联查询。
第一个示例展示了如何使用 Joins() 和 Rows() 方法进行左连接查询,返回的结果可以使用 rows.Next() 方法进行迭代。
第二个示例展示了如何使用 Joins() 和 Scan() 方法进行左连接查询,查询结果将被扫描到一个 struct slice 中。
第三个示例展示了如何进行多个关联查询,使用 Joins() 方法多次调用即可。注意,在多个关联查询中使用 AND 关键字连接多个条件。最后使用 Find() 方法获取查询结果并将其存储到 user 变量中。
3.9、Pluck
Pluck
方法可以用于从数据库中检索指定列的值,并将其填充到切片中。=> 查询多个列,可以用Scan代替
var ages []int64
db.Find(&users).Pluck("age", &ages)
var names []string
db.Model(&User{}).Pluck("name", &names)
db.Table("deleted_users").Pluck("name", &names)
// Requesting more than one column? Do it like this:
db.Select("name, age").Find(&users)
// 这段代码是使用 GORM 进行查询并提取某些字段的值。其中:
db.Find(&users) 会查询所有的 User 记录并存入 users 切片中。
db.Pluck("age", &ages) 会从查询结果中提取 age 字段的值并存入 ages 切片中。
db.Model(&User{}).Pluck("name", &names) 会从 users 表中仅查询 name 字段的值并存入 names 切片中。
db.Table("deleted_users").Pluck("name", &names) 会从 deleted_users 表中仅查询 name 字段的值并存入 names 切片中。
db.Select("name, age").Find(&users) 会查询 User 记录的 name 和 age 字段的值,并将结果存入 users 切片中。
3.10、Scan
将Scan查询结构放入到另外一个结构体中
type Result struct {
Name string
Age int
}
var result Result
db.Table("users").Select("name, age").Where("name = ?", 3).Scan(&result)
// Raw SQL
db.Raw("SELECT name, age FROM users WHERE name = ?", 3).Scan(&result)
4、更新
4.1、更新所有字段
Save方法在执行SQL更新操作时将包含所有字段,即使这些字段没有被修改。
db.First(&user)
user.Name = "zhihui 2"
user.Age = 100
db.Save(&user)
// = update users set name = "zhihui 2", age = 100, birthday = "2000-04-25", updated_at = "2022-4-28 10:00:00" where id = 111;
4.2、更新已更改的字段
// 如果单个属性被更改了,更新它
db.Model(&user).Update("name", "hello")
// UPDATE users SET name='hello', updated_at='2013-11-17 21:34:10' WHERE id=111;
// 使用组合条件更新单个属性
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;
// 使用 `map` 更新多个属性,只会更新那些被更改了的字段
db.Model(&user).Updates(map[string]interface{}{"name": "hello", "age": 18, "actived": false})
// UPDATE users SET name='hello', age=18, actived=false, updated_at='2013-11-17 21:34:10' WHERE id=111;
// 使用 `struct` 更新多个属性,只会更新那些被修改了的和非空的字段
db.Model(&user).Updates(User{Name: "hello", Age: 18})
// UPDATE users SET name='hello', age=18, updated_at = '2013-11-17 21:34:10' WHERE id = 111;
// 警告: 当使用结构体更新的时候, GORM 只会更新那些非空的字段
// 例如下面的更新,没有东西会被更新,因为像 "", 0, false 是这些字段类型的空值
db.Model(&user).Updates(User{Name: "", Age: 0, Actived: false})
4.3、更新选中字段
如果我们在执行更新操作的时候只想更新或者忽略某些字段,那么可以使用select、omit方法:
db.Model(&user).Select("name").Updates(map[string]interface{}{"name": "hello", "age": 18, "actived": false})
// UPDATE users SET name='hello', updated_at='2013-11-17 21:34:10' WHERE id=111;
db.Model(&user).Omit("name").Updates(map[string]interface{}{"name": "hello", "age": 18, "actived": false})
// UPDATE users SET age=18, actived=false, updated_at='2013-11-17 21:34:10' WHERE id=111;
4.4、更新列钩子方法
上面的更新操作,在更新的时候会执行模型的BeforeUpdate 和 AtferUpdate方法,来更新UpdatedAt时间戳,并且保存它的关联,如果不想执行这些操作,可以用UpdateColumn,UpdateColumns方法。如下👇
// Update single attribute, similar with `Update`
db.Model(&user).UpdateColumn("name", "hello")
// UPDATE users SET name='hello' WHERE id = 111;
// Update multiple attributes, similar with `Updates`
db.Model(&user).UpdateColumns(User{Name: "hello", Age: 18})
// UPDATE users SET name='hello', age=18 WHERE id = 111;
4.5、批量更新
批量更新的时候,钩子函数不会执行。
db.Table("users").Where("id IN (?)", []int{10, 11}).Updates(map[string]interface{}{"name": "hello", "age": 18})
// UPDATE users SET name='hello', age=18 WHERE id IN (10, 11);
// 使用结构体更新将只适用于非零值,或者使用 map[string]interface{}
db.Model(User{}).Updates(User{Name: "hello", Age: 18})
// UPDATE users SET name='hello', age=18;
// 使用 `RowsAffected` 获取更新影响的记录数
db.Model(User{}).Updates(User{Name: "hello", Age: 18}).RowsAffected
4.6、带有表达式的SQL更新
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" = '2';
DB.Model(&product).Updates(map[string]interface{}{"price": gorm.Expr("price * ? + ?", 2, 100)})
// UPDATE "products" SET "price" = price * '2' + '100', "updated_at" = '2013-11-17 21:34:10' WHERE "id" = '2';
DB.Model(&product).UpdateColumn("quantity", gorm.Expr("quantity - ?", 1))
// UPDATE "products" SET "quantity" = quantity - 1 WHERE "id" = '2';
DB.Model(&product).Where("quantity > 1").UpdateColumn("quantity", gorm.Expr("quantity - ?", 1))
// UPDATE "products" SET "quantity" = quantity - 1 WHERE "id" = '2' AND quantity > 1;
4.7、在钩子函数中更新值
如果你想使用BeforeUpdate、BeforeSave钩子函数修改更新的值,可以使用scope.SetColumn方法,如:
func (user *User) BeforeSave(scope *gorm.Scope) (err error) {
if pw, err := bcrypt.generateFromPassword(suer.Password, 0); err != nil {
scope.SetColumn("Encryptedpassword", pw)
}
}
// 这是一个 GORM 中的 BeforeSave 钩子函数,它会在执行保存操作之前被自动调用。在这个函数中,可以对当前对象的数据进行修改或者添加一些额外的操作,例如在这个例子中对用户密码进行加密。
函数签名中的 scope *gorm.Scope 参数表示当前对象的作用域,可以使用它来访问和修改当前对象的属性和关联关系。
在这个例子中,函数通过调用 bcrypt 包中的 GenerateFromPassword 函数,将用户的密码进行了加密,并将加密后的结果设置到 Encryptedpassword 字段中。
这样,在保存用户对象时,GORM 就会自动将加密后的密码保存到数据库中。
4.8、额外的更新选项
// 在更新 SQL 语句中添加额外的 SQL 选项
db.Model(&user).Set("gorm:update_option", "OPTION (OPTIMIZE FOR UNKNOWN)").Update("name", "hello")
UPDATE users SET name='hello', updated_at = '2013-11-17 21:34:10' WHERE id=111 OPTION (OPTIMIZE FOR UNKNOWN);
// 在GORM中,Set函数用于设置更新操作的其他选项。在这个例子中,"gorm:update_option", "OPTION (OPTIMIZE FOR UNKNOWN)"用于设置查询优化选项。
具体来说,OPTION (OPTIMIZE FOR UNKNOWN)是一种查询提示语法,它告诉数据库优化器,不要基于参数值来编写查询计划,而是假定参数值是未知的,从而生成更通用的查询计划。这有助于避免由于查询参数不同而导致的性能问题。
Update("name", "hello")则表示将user对象的name字段更新为"hello"。
5、删除
5.1、删除记录
注意:删除一条记录的时候,需要确定主键是有值的,gorm会使用主键删除这条记录,如果主键字段为空,gorm会删除模型中的所有记录!!!
// 删除一条存在的记录
db.Delete(&email)
DELETE from emails where id=10;
// 为删除 SQL 语句添加额外选项
db.Set("gorm:delete_option", "OPTION (OPTIMIZE FOR UNKNOWN)").Delete(&email)
DELETE from emails where id=10 OPTION (OPTIMIZE FOR UNKNOWN);
5.2、批量删除
删除所有符合条件的记录
db.Where("email LIKE ?", "%zhihui%").Delete(Email{})
// DELETE from emails where email LIKE "%zhihui%";
db.Delete(Email{}, "email LIKE ?", "%zhihui%")
// DELETE from emails where email LIKE "%zhihui%";
5.3、软删除
如果模型中有DeletedAt字段
,它将自动拥有软删除的能力;当执行删除操作的时候,数据不会真正的删除,而是将DeletedAt更新为当前的时间
db.Delete(&user)
// UPDATE users SET deleted_at="2013-10-29 10:23" WHERE id = 111;
// 批量删除
db.Where("age = ?", 20).Delete(&User{})
// UPDATE users SET deleted_at="2013-10-29 10:23" WHERE age = 20;
// 在查询记录时,软删除记录会被忽略
db.Where("age = 20").Find(&user)
// SELECT * FROM users WHERE age = 20 AND deleted_at IS NULL;
// 使用 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;
6、扩展(钩子)
在 Go 中,钩子(Hook)是一种函数,它可以在程序执行过程中被调用,以便在特定的代码段中添加自定义的逻辑。通常,钩子被用于在某个事件发生时自动触发某些操作,或者在某个代码段执行前或执行后添加一些特定的行为。
钩子函数(Hook Function)就是实现了特定功能的钩子。在 Go 中,钩子函数通常以一种约定的方式命名,比如在函数名前面加上 “Before” 或 “After” 前缀,以表明它们是在某个事件发生之前还是之后被调用的。常见的钩子函数包括 BeforeCreate
、AfterCreate
、BeforeUpdate
、AfterUpdate
等,它们会在创建或更新数据库记录时自动被调用。开发者可以在这些钩子函数中添加自己的逻辑,比如对记录的数据进行加密、计算某些值、添加日志等操作。\
在本文中,我们详细的说明了有关gorm中数据库的基本操作,包括增删改查四个方面,代码量多,但是好理解,希望在条件允许的条件下,去动手实践,不仅仅是观看,下面计划是
对gorm中的关联部分
进行总结