【Gorm】数据库基本操作-CRUD接口

文章详细介绍了GORM库在Go语言中进行数据库操作的方法,包括创建记录、查询(简单查询、Where条件、Not、Or、行内查询条件)、更新(更新所有字段、更新已更改的字段、更新选中字段、批量更新、带有表达式的SQL更新)和删除记录(单个删除、批量删除、软删除)。此外,还讨论了如何在钩子函数中设置字段值和使用子查询、分页、排序、计数等高级查询技巧。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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” 前缀,以表明它们是在某个事件发生之前还是之后被调用的。常见的钩子函数包括 BeforeCreateAfterCreateBeforeUpdateAfterUpdate 等,它们会在创建或更新数据库记录时自动被调用。开发者可以在这些钩子函数中添加自己的逻辑,比如对记录的数据进行加密、计算某些值、添加日志等操作。\

在本文中,我们详细的说明了有关gorm中数据库的基本操作,包括增删改查四个方面,代码量多,但是好理解,希望在条件允许的条件下,去动手实践,不仅仅是观看,下面计划是对gorm中的关联部分进行总结

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Coder陈、

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值