📦GORMv1.25.6
🍅 增
🌟 创建一条数据
使用 Create
来向对应数据库创建一条数据,可以通过它的返回值获取错误和插入记录
user := &User{
Name: "tanchang",
Password: "123456",
}
//插入字段
result := db.Create(user)
if result.Error != nil {
println("插入错误", result.Error)
}
fmt.Printf("擦插入成功,插入了: %d 条数据", result.RowsAffected)
插入多个字段
//插入多个字段
users := []*User{
{Name: "tanchang", Password: "123456"},
{Name: "szy", Password: "123456"},
}
result := db.Create(users)
if result.Error != nil {
println("插入错误", result.Error)
}
fmt.Printf("擦插入成功,插入了: %d 条数据", result.RowsAffected)
🌟 通过指定字段创建\忽略字段
通过指定字段创建记录,这里就只会创建 Password
//指定字段插入
user := &User{
Password: "23456",
Name: "bai"
}
tx := db.Select("Password").Create(user)
if tx.Error != nil {
println("插入错误", tx.Error)
}
忽略字段,这里就是会创建 Name
字段
user := &User{
Password: "23456",
Name: "bai",
}
tx := db.Omit("Password").Create(user)
if tx.Error != nil {
println("插入错误", tx.Error)
}
🌟 批量插入
前面提到了可以使用切片来创建多个字段,传入切片后
GORM
将生成一条 SQL
来插入所有数据,以此返回所有主键值,并触发Hook方法。如果这些数据分成多批执行,GORM
会开启一个事物来处理它们;
数据库事务是一系列操作,它们作为一个整体执行,具有以下四个重要的属性(通常被称为 ACID 属性):
- 原子性(Atomicity):事务中的所有操作要么全部完成,要么全部不完成,不会结束在中间某个点。
- 一致性(Consistency):事务必须保证数据库从一个一致的状态转移到另一个一致的状态。
- 隔离性(Isolation):并发执行的事务之间不会互相影响。
- 持久性(Durability):事务一旦提交,它对数据库的改变就是永久性的,即使系统发生故障也不会丢失。
接下来设置批次处理,使用初始化或者 CreateInBatches
//可以在初始时候配置
db, err := gorm.Open(sqlite.Open("gorm.db"), &gorm.Config{
CreateBatchSize: 1000,
})
//也可以使用CreateInBatches
// batch size 100
db.CreateInBatches(users, 100)
🌟 Hook
Grom
提供了很多 hook
比如 BeforeSave
,BeforeCreate
,AfterSave
,AfterCreate
这些接口来定义 Hook
,这个后面在学
🌟 通过 Map
创建
⚠️ 使用map来创建时,钩子方法就不会执行,关联并不会被保存,而且还不会写回主键!
由于是通过map创建不是基于一个 Model
,所以首先要指定 Model
tx := db.Model(&model.User{}).Create(map[string]interface{}{
"name": "张三",
"password": "123456",
})
if tx.Error != nil {
println("创建错误", tx.Error)
}
🍅 查
⭐️ 查询数据必然会返回数据,你可以使用结构体来接收,也可以使用 map
来接收这个数据
🌟 通过 First
,Take
,Last
查询一条对象
1️⃣ First
First
获取通过主键排序后的第一条记录
//定义接收对象
var data map[string]interface{}
db.Model(&User{}).First(&data)
fmt.Println(data)
2️⃣ Take
直接获取第一条对象
db.Model(&User{}).Take(&data)
fmt.Println(data)
3️⃣ Last
查询最后一条,First
and Last
方法会按主键排序找到第一条记录和最后一条记录 (分别)。 只有在目标 struct
是指针或者通过 db.Model()
指定 model 时,该方法才有效
db.Model(&User{}).Last(&data)
fmt.Println(data)
🌟 使用 Find
查询全部对象
由于有多个对象,需要数组或者切片来接收
var u []User
tx := db.Model(&User{}).Find(&u)
if tx.Error != nil {
println("查询失败", tx.Error)
}
fmt.Println(u)
🌟 条件查找
1️⃣ 主键检索
通过主键来查询
var u []User
//查询主键为5的数据
tx := db.Model(&User{}).Find(&u, 5)
if tx.Error != nil {
println("查询失败", tx.Error)
}
fmt.Println(u)
2️⃣ 通过 Where
过滤条件查找
它可以接收字符串,前面为查找的列,后面为查找的行
// 检索
tx := db.Model(&User{}).Where("name = ?", "张三").Find(&u)
if tx.Error != nil {
println("检索失败", tx.Error)
}
fmt.Println(u)
🏢 官方示例
// Get first matched record
db.Where("name = ?", "jinzhu").First(&user)
// SELECT * FROM users WHERE name = 'jinzhu' ORDER BY id LIMIT 1;
// Get all matched records
db.Where("name <> ?", "jinzhu").Find(&users)
// SELECT * FROM users WHERE name <> 'jinzhu';
// IN
db.Where("name IN ?", []string{"jinzhu", "jinzhu 2"}).Find(&users)
// SELECT * FROM users WHERE name IN ('jinzhu','jinzhu 2');
// LIKE
db.Where("name LIKE ?", "%jin%").Find(&users)
// SELECT * FROM users WHERE name LIKE '%jin%';
// AND
db.Where("name = ? AND age >= ?", "jinzhu", "22").Find(&users)
// SELECT * FROM users WHERE name = 'jinzhu' AND age >= 22;
// Time
db.Where("updated_at > ?", lastWeek).Find(&users)
// SELECT * FROM users WHERE updated_at > '2000-01-01 00:00:00';
// BETWEEN
db.Where("created_at BETWEEN ? AND ?", lastWeek, today).Find(&users)
// SELECT * FROM users WHERE created_at BETWEEN '2000-01-01 00:00:00' AND '2000-01-08 00:00:00';
还可以接收 map
和结构体
var u []User
// 检索
db.Model(&User{}).Where(&User{Name: "李四"}).Find(&u)
fmt.Println(u)
db.Model(&User{}).Where(map[string]interface{}{
"name": "王五",
}).Find(&u)
fmt.Println(u)
还可以指定结构体来查询对象
var u []User
// 检索
tx := db.Model(&User{}).Where(&User{
Name: "张三",
}, "name").Find(&u)
if tx.Error != nil {
println("检索失败", tx.Error)
}
fmt.Println(u)
3️⃣ 内联条件查询
// Get by primary key if it were a non-integer type
db.First(&user, "id = ?", "string_primary_key")
// SELECT * FROM users WHERE id = 'string_primary_key';
// Plain SQL
db.Find(&user, "name = ?", "jinzhu")
// SELECT * FROM users WHERE name = "jinzhu";
db.Find(&users, "name <> ? AND age > ?", "jinzhu", 20)
// SELECT * FROM users WHERE name <> "jinzhu" 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;
4️⃣ Not
条件查询
查询不存在这个字段的,它接收的字段和 Where
一样
//查询除了名字为张三以外的所有字段
var u []User
// 检索
tx := db.Model(&User{}).Not(&User{
Name: "张三",
}, "name").Find(&u)
if tx.Error != nil {
println("检索失败", tx.Error)
}
fmt.Println(u)
5️⃣ Or
条件
它接收的字段也和 Where
一样,一般情况下和Where一起使用
//查询名字为张三或者王五的字段
var u []User
// 检索
tx := db.Model(&User{}).Where(&User{
Name: "张三",
}, "name").Or(&User{
Name: "王五",
}).Find(&u)
if tx.Error != nil {
println("检索失败", tx.Error)
}
for _, d := range u {
fmt.Println(d)
}
6️⃣ 选择特定字段查找
使用 Select
,它只会返回你选中的字段,其他字段或过滤掉
var u []User
// 检索
tx := db.Model(&User{}).Select("password").Find(&u)
if tx.Error != nil {
println("检索失败", tx.Error)
}
for _, d := range u {
fmt.Println(d)
}
7️⃣ Order
排序
可以选定字段进行排序后查询
var u []User
// 检索
tx := db.Model(&User{}).Order("name").Find(&u)
if tx.Error != nil {
println("检索失败", tx.Error)
}
for _, d := range u {
fmt.Println(d)
}
8️⃣ 限制和偏移
Limit
指定要检索的最大记录数 Offset
指定在开始返回记录之前要跳过的记录数
db.Limit( 3 ).Find(&users)
//从用户中选择*LIMIT 3;
// 用 -1 取消限制条件
db.Limit( 10 ).Find(&users1).Limit( -1 ).Find(&users2)
// SELECT * FROM users LIMIT 10; (users1)
// SELECT * FROM users; (users2)
db.Offset( 3 ).Find(&users)
//从用户偏移 3 中选择*;
db.Limit( 10 ).Offset( 5 ).Find(&users)
// SELECT * FROM users OFFSET 5 LIMIT 10;
// 用 -1 取消偏移条件
db.Offset( 10 ).Find(&users1).Offset( -1 ).Find(&users2)
// SELECT * FROM users OFFSET 10; (users1)
// SELECT * FROM users; (users2)
9️⃣ 联表查询
https://gorm.io/zh_CN/docs/query.html#Joins
最好时先去了解一下联表查询
type result struct {
Id int64
Title string
}
var res result
// 检索
db.Model(&model.User{}).Select("gormuser.id,gormtasks.title").Joins("left join gormtasks on gormuser.name = gormtasks.user_name").Scan(&res)
fmt.Println(res)
🌟 高级查询
1️⃣ 智能选择字段
在 GORM
中,您可以使用 Select
方法有效地选择特定字段。 这在Model字段较多但只需要其中部分的时候尤其有用,比如编写API响应
type result struct {
Id int64
Name string
}
var res []result
db.Model(&model.User{}).Find(&res)
for _, v := range res {
println(v.Id, v.Name)
}
输出:
5 张三
6 李四
7 王五
注意 在
QueryFields
模式中, 所有的模型字段(model fields)都会被根据他们的名字选择。
db, err := gorm.Open(sqlite.Open("gorm.db"), &gorm.Config{
QueryFields: true,
})
// 当 QueryFields 被设置为 true 时,此行为默认进行
db.Find(&user)
// SQL: SELECT `users`.`name`, `users`.`age`, ... FROM `users`
// 开启 QueryFields 并使用会话模式(Session mode)
db.Session(&gorm.Session{QueryFields: true}).Find(&user)
// SQL: SELECT `users`.`name`, `users`.`age`, ... FROM `users`
🏢 其他高级操作请查看官网:https://gorm.io/zh_CN/docs/advanced_query.html
🍅 改
🌟 使用 Save
来保存修改的字段
需要获取到 model
之后才可以使用
var res model.User
db.Model(&model.User{}).First(&res)
res.Name = "tanc"
tx := db.Save(&res)
if tx.Error != nil {
println("更新失败", tx.Error)
}
如果保存值不包含主键,它将执行 Create
,否则它将执行 Update
(包含所有字段)
db.Save(&User{Name: "jinzhu", Age: 100})
// INSERT INTO `users` (`name`,`age`,`birthday`,`update_at`) VALUES ("jinzhu",100,"0000-00-00 00:00:00","0000-00-00 00:00:00")
db.Save(&User{ID: 1, Name: "jinzhu", Age: 100})
// UPDATE `users` SET `name`="jinzhu",`age`=100,`birthday`="0000-00-00 00:00:00",`update_at`="0000-00-00 00:00:00" WHERE `id` = 1
⚠️ 不要将 Save
和 Model
一同使用, 这是 未定义的行为
🌟 更新单个列
不需要获取 model
,直接指定 model
,还可以通过 Where
、Or
、Select
,Omit
等其他语句来过滤一下在,
tx := db.Model(&model.User{}).Where("id = ?", 6).Update("name", "szy")
if tx.Error != nil {
println("更新失败", tx.Error)
}
或者直接依靠传入的 model
//创建接收模型
var user model.User
//获取模型
db.First(&user)
//更新模型
tx := db.Model(&user).Update("password", "000000")
if tx.Error != nil {
println("更新失败", tx.Error)
}
两者也可以结合
var user model.User
db.First(&user)
tx := db.Model(&user).Where("id = ?", 1).Update("password", "000000")
if tx.Error != nil {
println("更新失败", tx.Error)
}
🌟 更新多个列
使用 updates
使用方法还是和 update
差不多,Updates
方法支持 struct
和 map[string]interface{}
参数。当使用 struct
更新时,默认情况下 GORM
只会更新非零值的字段
// 根据 `struct` 更新属性,只会更新非零值的字段
db.Model(&user).Updates(User{Name: "hello", Age: 18, Active: false})
// UPDATE users SET name='hello', age=18, updated_at = '2013-11-17 21:34:10' WHERE id = 111;
// 根据 `map` 更新属性
db.Model(&user).Updates(map[string]interface{}{"name": "hello", "age": 18, "active": false})
// UPDATE users SET name='hello', age=18, active=false, updated_at='2013-11-17 21:34:10' WHERE id=111;
var user []model.User
db.Find(&user)
tx := db.Model(&user).Where("id like ?", "%").Updates(map[string]interface{}{"name": "tanc", "password": "123456"})
if tx.Error != nil {
println("更新失败", tx.Error)
}
🏢 更多操作可以查看官网https://gorm.io/zh_CN/docs/update.html
🍅 删
🌟 删除操作
删除一条记录时,删除对象需要指定主键,否则会触发 批量删除
⚠️ 如果你的模型包含了 gorm.DeletedAt
字段(该字段也被包含在 gorm.Model
中),那么该模型将会自动获得软删除的能力!当调用 Delete
时,GORM
并不会从数据库中删除该记录,而是将该记录的 DeleteAt
设置为当前时间,而后的一般查询方法将无法查找到此条记录
var user model.User
db.First(&user)
tx := db.Model(&model.User{}).Delete(&user)
if tx.Error != nil {
println("删除失败", tx.Error)
}
所以你在删除后,并不会在表中直接删除,此时你在去查找就会报错了,因为数据都被删除了,找不到数据了
MariaDB [gorm]> select * from gormuser;
+------+----------+----+-------------------------+-------------------------+-------------------------+
| name | password | id | created_at | updated_at | deleted_at |
+------+----------+----+-------------------------+-------------------------+-------------------------+
| tanc | 123456 | 5 | 0000-00-00 00:00:00.000 | 2024-06-27 18:08:13.649 | 2024-06-27 22:09:35.637 |
| tanc | 123456 | 6 | NULL | 2024-06-27 18:08:13.649 | 2024-06-27 23:16:48.541 |
| tanc | 123456 | 7 | NULL | 2024-06-27 18:08:13.649 | 2024-06-27 23:17:17.669 |
+------+----------+----+-------------------------+-------------------------+-------------------------+
3 rows in set (0.000 sec)
可以使用 Unscoped
来查询到被软删除的记录
var user []model.User
db.Unscoped().Find(&user)
for _, d := range user {
fmt.Println(d)
}
也可以使用它来永久删除,这样就清除掉软删除的数据了
var user []model.User
db.Unscoped().Find(&user).Delete(&user)
🌟 恢复软删除
var userdoc []UserDoc
db.Unscoped().Find(&userdoc)
//设置为nil
db.Unscoped().Model(&userdoc).Update("DeletedAt", nil)