CRUD 基础操作
第四章:CRUD 基础操作
4.1 创建(Create)
单条记录创建
user := User{Name: "张三", Email: "zhangsan@example.com", Age: 25}
result := db.Create(&user)
// 获取结果
fmt.Println(user.ID) // 返回主键
fmt.Println(result.Error) // 返回错误
fmt.Println(result.RowsAffected) // 返回影响的记录数
指定字段创建
// 只创建 Name 字段
db.Select("Name").Create(&user)
// INSERT INTO `users` (`name`) VALUES ("张三")
// 忽略 Email 字段
db.Omit("Email").Create(&user)
// INSERT INTO `users` (`name`,`age`) VALUES ("张三", 25)
批量创建
users := []User{
{Name: "张三", Email: "zhangsan@example.com"},
{Name: "李四", Email: "lisi@example.com"},
{Name: "王五", Email: "wangwu@example.com"},
}
// 方式1:传递切片
db.Create(&users)
// 方式2:指定批次大小
db.CreateInBatches(users, 100)
根据 Map 创建
// 单条
db.Model(&User{}).Create(map[string]interface{}{
"Name": "张三",
"Age": 25,
})
// 批量
db.Model(&User{}).Create([]map[string]interface{}{
{"Name": "张三", "Age": 25},
{"Name": "李四", "Age": 28},
})
关联创建
type CreditCard struct {
gorm.Model
Number string
UserID uint
}
type User struct {
gorm.Model
Name string
CreditCard CreditCard
}
// 同时创建用户和信用卡
db.Create(&User{
Name: "张三",
CreditCard: CreditCard{Number: "4111111111111111"},
})
// 先 INSERT INTO users,再 INSERT INTO credit_cards
4.2 查询(Read)
主键查询
// 查询第一条记录(按主键排序)
db.First(&user)
// SELECT * FROM users ORDER BY id LIMIT 1
// 查询最后一条记录
db.Last(&user)
// SELECT * FROM users ORDER BY id DESC LIMIT 1
// 根据主键查询
db.First(&user, 10)
// SELECT * FROM users WHERE id = 10
// 根据多个主键查询
db.Find(&users, []int{1, 2, 3})
// SELECT * FROM users WHERE id IN (1,2,3)
获取所有记录
var users []User
db.Find(&users)
// SELECT * FROM users
条件查询
// 获取第一条匹配记录
db.Where("name = ?", "张三").First(&user)
// 获取所有匹配记录
db.Where("name <> ?", "张三").Find(&users)
// 多条件
db.Where("name = ? AND age >= ?", "张三", 18).Find(&users)
// 结构体条件(零值不参与查询)
db.Where(&User{Name: "张三", Age: 0}).Find(&users)
// SELECT * FROM users WHERE name = "张三"
// Map 条件
db.Where(map[string]interface{}{"name": "张三", "age": 25}).Find(&users)
// 主键切片
db.Where([]int64{1, 2, 3}).Find(&users)
内联条件
// 非整数主键也可以用 First/Last
db.First(&user, "id = ?", "string-primary-key")
// 其他条件
db.Find(&users, "name = ?", "张三")
db.Find(&users, "name <> ? AND age > ?", "张三", 18)
db.Find(&users, User{Age: 25})
db.Find(&users, map[string]interface{}{"age": 25})
Not 条件
db.Not("name = ?", "张三").First(&user)
db.Not(map[string]interface{}{"name": []string{"张三", "李四"}}).Find(&users)
db.Not([]int64{1, 2, 3}).First(&user)
Or 条件
db.Where("role = ?", "admin").Or("role = ?", "super_admin").Find(&users)
db.Where("name = ?", "张三").Or(User{Name: "李四"}).Find(&users)
选择特定字段
db.Select("name", "age").Find(&users)
db.Select([]string{"name", "age"}).Find(&users)
// SELECT name, age FROM users
// 使用 SQL 函数
db.Select("name, sum(age) as total_age").Group("name").Find(&results)
排序
db.Order("age desc, name").Find(&users)
db.Order("age desc").Order("name").Find(&users)
db.Clauses(clause.OrderBy{
Expression: clause.Expr{SQL: "FIELD(id,?)", Vars: []interface{}{[]int{1, 2, 3}}, WithoutParentheses: true},
}).Find(&User{})
// SELECT * FROM users ORDER BY FIELD(id,1,2,3)
Limit & Offset
db.Limit(3).Find(&users)
db.Limit(10).Offset(5).Find(&users)
db.Limit(-1).Find(&users) // 取消 Limit
db.Offset(-1).Find(&users) // 取消 Offset
Distinct
db.Distinct("name", "age").Order("name, age desc").Find(&results)
// SELECT DISTINCT name, age FROM users ORDER BY name, age desc
Pluck(查询单个字段)
var names []string
db.Model(&User{}).Pluck("name", &names)
// SELECT name FROM users
var ages []int64
db.Model(&User{}).Pluck("age", &ages)
// 去重
db.Model(&User{}).Distinct().Pluck("name", &names)
// 多列 Pluck
_db.Select("name", "age").Find(&users)
4.3 更新(Update)
保存所有字段
db.First(&user)
user.Name = "张三_new"
user.Age = 30
db.Save(&user)
// UPDATE users SET name='张三_new', age=30, updated_at='...' WHERE id=1
更新单个字段
db.Model(&User{}).Where("active = ?", true).Update("name", "hello")
// UPDATE users SET name='hello', updated_at='...' WHERE active=true
更新多列
// 结构体(零值不参与更新)
db.Model(&user).Updates(User{Name: "hello", Age: 18, Active: false})
// UPDATE users SET name='hello', age=18, updated_at = '...' WHERE id = 1;
// Map(零值也会更新)
db.Model(&user).Updates(map[string]interface{}{"name": "hello", "age": 18, "active": false})
更新选定字段
db.Model(&user).Select("name").Updates(map[string]interface{}{"name": "hello", "age": 18})
// 只更新 name,忽略 age
db.Model(&user).Omit("name").Updates(map[string]interface{}{"name": "hello", "age": 18})
// 更新除 name 外的所有字段
批量更新
db.Model(User{}).Where("role = ?", "user").Update("role", "admin")
// UPDATE users SET role='admin' WHERE role='user'
// 使用 Session 防止全局更新(GORM v2 默认阻止无 Where 的更新)
db.Session(&gorm.Session{AllowGlobalUpdate: true}).Model(&User{}).Update("name", "zhangsan")
使用 SQL 表达式更新
db.Model(&product).UpdateColumn("quantity", gorm.Expr("quantity - ?", 1))
// UPDATE products SET quantity = quantity - 1 WHERE id = 1
db.Model(&user).UpdateColumn("age", gorm.Expr("age + ?", 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 = ?", "张三").Update("company_name", db.Table("companies as c").Select("name").Where("c.id = u.company_id"))
4.4 删除(Delete)
删除单条记录
var user User
db.First(&user, 1)
db.Delete(&user)
// UPDATE users SET deleted_at='...' WHERE id=1
// 软删除(如果模型包含 gorm.DeletedAt)
// DELETE FROM users WHERE id=1
// 物理删除(如果不含 DeletedAt)
根据主键删除
db.Delete(&User{}, 10)
db.Delete(&User{}, "10")
db.Delete(&users, []int{1, 2, 3})
批量删除
db.Where("email LIKE ?", "%@example.com%").Delete(&Email{})
db.Delete(Email{}, "email LIKE ?", "%@example.com%")
软删除(GORM v2 默认)
// 查询时会自动过滤软删除记录
db.Where("age = ?", 20).Find(&user)
// SELECT * FROM users WHERE age = 20 AND deleted_at IS NULL
// 包含软删除记录
db.Unscoped().Where("age = 20").Find(&users)
// SELECT * FROM users WHERE age = 20
物理删除
db.Unscoped().Delete(&order)
// DELETE FROM orders WHERE id=10
永久删除匹配的记录
db.Unscoped().Where("status = ?", "cancelled").Delete(&Order{})
4.5 高级查询技巧
Scan
type Result struct {
Name string
Age int
}
var result Result
db.Table("users").Select("name", "age").Where("name = ?", "张三").Scan(&result)
// 扫描到 map
var results []map[string]interface{}
db.Table("users").Find(&results)
行/原始 SQL
// 原生 SQL
db.Raw("SELECT name, age FROM users WHERE name = ?", "张三").Scan(&result)
// 执行原生 SQL
db.Exec("DROP TABLE users")
db.Exec("UPDATE users SET age = ? WHERE name = ?", 25, "张三")
// 游标遍历
rows, err := db.Model(&User{}).Where("name = ?", "张三").Rows()
defer rows.Close()
for rows.Next() {
var user User
db.ScanRows(rows, &user)
// 处理 user
}
4.6 完整示例代码
package main
import (
"fmt"
"gorm.io/driver/sqlite"
"gorm.io/gorm"
"gorm.io/gorm/logger"
"log"
"os"
)
type Product struct {
gorm.Model
Code string `gorm:"uniqueIndex"`
Price uint
}
func main() {
// 初始化
db, err := gorm.Open(sqlite.Open("test.db"), &gorm.Config{
Logger: logger.New(log.New(os.Stdout, "\r\n", log.LstdFlags), logger.Config{
LogLevel: logger.Info,
}),
})
if err != nil {
panic("failed to connect database")
}
// 迁移
db.AutoMigrate(&Product{})
// ===== 创建 =====
// 创建记录
product := Product{Code: "P001", Price: 100}
db.Create(&product)
fmt.Printf("Created: ID=%d\n", product.ID)
// 批量创建
products := []Product{
{Code: "P002", Price: 200},
{Code: "P003", Price: 300},
}
db.Create(&products)
// ===== 查询 =====
// 查询单条
var p Product
db.First(&p, 1) // 根据主键
db.First(&p, "code = ?", "P001") // 根据条件
fmt.Printf("Found: %+v\n", p)
// 查询多条
var allProducts []Product
db.Find(&allProducts)
fmt.Printf("Total: %d\n", len(allProducts))
// 条件查询
var cheapProducts []Product
db.Where("price < ?", 250).Find(&cheapProducts)
// ===== 更新 =====
// 更新单字段
db.Model(&p).Update("Price", 150)
// 更新多字段
db.Model(&p).Updates(Product{Price: 180, Code: "P001-new"})
// 更新所有匹配记录
db.Model(&Product{}).Where("price < ?", 300).Update("price", gorm.Expr("price * ?", 1.1))
// ===== 删除 =====
// 软删除
var toDelete Product
db.First(&toDelete, 3)
db.Delete(&toDelete)
// 查询包含软删除
var allWithDeleted []Product
db.Unscoped().Find(&allWithDeleted)
// 物理删除
db.Unscoped().Delete(&toDelete)
fmt.Println("Done!")
}
4.7 练习题
- 编写批量插入 10000 条记录的代码,比较逐条插入和批量插入的性能差异
- 实现一个分页查询函数,支持按任意字段排序
- 编写软删除恢复功能(将 deleted_at 设为 NULL)
4.8 小结
本章详细介绍了 GORM 的 CRUD 基础操作,包括各种查询方式、更新策略和删除模式。掌握这些基础是进行复杂数据操作的前提。
本文代码地址:https://github.com/LittleMoreInteresting/gorm_study
欢迎关注公众号,一起学习进步!