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 练习题

  1. 编写批量插入 10000 条记录的代码,比较逐条插入和批量插入的性能差异
  2. 实现一个分页查询函数,支持按任意字段排序
  3. 编写软删除恢复功能(将 deleted_at 设为 NULL)

4.8 小结

本章详细介绍了 GORM 的 CRUD 基础操作,包括各种查询方式、更新策略和删除模式。掌握这些基础是进行复杂数据操作的前提。


本文代码地址:https://github.com/LittleMoreInteresting/gorm_study

欢迎关注公众号,一起学习进步!

如有疑问关注公众号给我留言
wx

关注公众号

©2017-2023 鲁ICP备17023316号-1 Powered by Hugo