CRUD操作 #

一、创建操作 #

1.1 创建单条记录 #

go
func main() {
    r := gin.Default()
    
    r.POST("/users", func(c *gin.Context) {
        var user User
        if err := c.ShouldBindJSON(&user); err != nil {
            c.JSON(400, gin.H{"error": err.Error()})
            return
        }
        
        // 创建记录
        if err := DB.Create(&user).Error; err != nil {
            c.JSON(500, gin.H{"error": err.Error()})
            return
        }
        
        c.JSON(201, user)
    })
    
    r.Run()
}

1.2 批量创建 #

go
r.POST("/users/batch", func(c *gin.Context) {
    var users []User
    if err := c.ShouldBindJSON(&users); err != nil {
        c.JSON(400, gin.H{"error": err.Error()})
        return
    }
    
    // 批量创建
    if err := DB.Create(&users).Error; err != nil {
        c.JSON(500, gin.H{"error": err.Error()})
        return
    }
    
    c.JSON(201, users)
})

// 分批创建
r.POST("/users/batch-large", func(c *gin.Context) {
    var users []User
    if err := c.ShouldBindJSON(&users); err != nil {
        c.JSON(400, gin.H{"error": err.Error()})
        return
    }
    
    // 每批100条
    if err := DB.CreateInBatches(&users, 100).Error; err != nil {
        c.JSON(500, gin.H{"error": err.Error()})
        return
    }
    
    c.JSON(201, users)
})

1.3 创建时指定字段 #

go
// 只插入指定字段
DB.Select("Name", "Email").Create(&user)

// 排除指定字段
DB.Omit("Age").Create(&user)

1.4 FirstOrCreate #

go
// 查找或创建
user := User{Name: "Alice"}
result := DB.Where(User{Name: "Alice"}).FirstOrCreate(&user)
// user.ID > 0 表示已存在,= 0 表示新创建

二、查询操作 #

2.1 基本查询 #

go
// 查询所有
var users []User
DB.Find(&users)

// 查询单条
var user User
DB.First(&user, 1)          // 按主键查询
DB.First(&user, "id = ?", 1) // 按条件查询
DB.Take(&user)              // 取一条记录
DB.Last(&user)              // 取最后一条

// 检查是否存在
if errors.Is(DB.First(&user, 1).Error, gorm.ErrRecordNotFound) {
    // 记录不存在
}

2.2 条件查询 #

go
// WHERE条件
DB.Where("name = ?", "Alice").First(&user)
DB.Where("name <> ?", "Alice").Find(&users)
DB.Where("age > ?", 18).Find(&users)
DB.Where("age >= ?", 18).Find(&users)
DB.Where("age < ?", 18).Find(&users)
DB.Where("age <= ?", 18).Find(&users)
DB.Where("age IN ?", []int{18, 19, 20}).Find(&users)
DB.Where("name LIKE ?", "%ali%").Find(&users)
DB.Where("name LIKE ?", "ali%").Find(&users)

// AND条件
DB.Where("name = ? AND age >= ?", "Alice", 18).Find(&users)

// OR条件
DB.Where("name = ?", "Alice").Or("name = ?", "Bob").Find(&users)

// NOT条件
DB.Not("name", "Alice").Find(&users)

2.3 结构体和Map条件 #

go
// 结构体条件
DB.Where(&User{Name: "Alice", Age: 18}).First(&user)
// SELECT * FROM users WHERE name = "Alice" AND age = 18;

// Map条件
DB.Where(map[string]interface{}{"name": "Alice", "age": 18}).Find(&users)

// 主键切片
DB.Where([]int64{1, 2, 3}).Find(&users)

2.4 选择字段 #

go
// 选择指定字段
DB.Select("name", "age").Find(&users)
DB.Select("name, age").Find(&users)

// 使用函数
DB.Select("AVG(age) as avg_age").Find(&result)

2.5 排序 #

go
// 单字段排序
DB.Order("age desc").Find(&users)
DB.Order("age desc, name asc").Find(&users)

2.6 分页 #

go
// Limit和Offset
DB.Limit(10).Find(&users)
DB.Limit(10).Offset(10).Find(&users)

// 分页函数
func Paginate(page, pageSize int) func(db *gorm.DB) *gorm.DB {
    return func(db *gorm.DB) *gorm.DB {
        if page <= 0 {
            page = 1
        }
        if pageSize <= 0 {
            pageSize = 10
        }
        offset := (page - 1) * pageSize
        return db.Offset(offset).Limit(pageSize)
    }
}

// 使用分页
DB.Scopes(Paginate(1, 10)).Find(&users)

2.7 分组 #

go
type Result struct {
    Name  string
    Total int
}

// GROUP BY
DB.Model(&User{}).Select("name, count(*) as total").Group("name").Find(&results)

// HAVING
DB.Model(&User{}).Select("name, count(*) as total").Group("name").Having("count(*) > ?", 1).Find(&results)

2.8 关联查询 #

go
// 预加载
DB.Preload("Posts").Find(&users)
DB.Preload("Posts").Preload("Profile").Find(&users)

// 条件预加载
DB.Preload("Posts", "title LIKE ?", "%go%").Find(&users)

// 嵌套预加载
DB.Preload("Posts.Comments").Find(&users)

// Joins预加载
DB.Joins("Posts").Find(&users)

2.9 统计 #

go
var count int64
DB.Model(&User{}).Count(&count)

// 条件统计
DB.Model(&User{}).Where("age > ?", 18).Count(&count)

// 分组统计
DB.Model(&User{}).Select("count(*) as count, age").Group("age").Find(&results)

三、更新操作 #

3.1 更新单个字段 #

go
// 更新单个字段
DB.Model(&user).Update("name", "Alice Updated")

// 条件更新
DB.Model(&User{}).Where("id = ?", 1).Update("name", "Alice Updated")

// 更新多个字段
DB.Model(&user).Updates(User{Name: "Alice", Age: 20})
DB.Model(&user).Updates(map[string]interface{}{"name": "Alice", "age": 20})

3.2 更新整个模型 #

go
// 更新所有字段
user.Name = "Alice Updated"
user.Age = 20
DB.Save(&user)

3.3 更新表达式 #

go
// 更新表达式
DB.Model(&user).Update("age", gorm.Expr("age + ?", 1))
DB.Model(&user).Update("age", gorm.Expr("age * ?", 2))

3.4 批量更新 #

go
// 批量更新
DB.Model(&User{}).Where("age > ?", 18).Updates(map[string]interface{}{"status": "adult"})

// 更新所有
DB.Model(&User{}).Where("1 = 1").Updates(map[string]interface{}{"status": "active"})

3.5 更新选中字段 #

go
// 只更新选中字段
DB.Model(&user).Select("Name").Updates(map[string]interface{}{"name": "Alice", "age": 20})

// 排除字段
DB.Model(&user).Omit("Age").Updates(map[string]interface{}{"name": "Alice", "age": 20})

四、删除操作 #

4.1 删除记录 #

go
// 删除单条记录
DB.Delete(&user, 1)
DB.Delete(&user, "id = ?", 1)

// 条件删除
DB.Where("name = ?", "Alice").Delete(&User{})

// 批量删除
DB.Where("age < ?", 18).Delete(&User{})

4.2 软删除 #

go
type User struct {
    ID        uint
    Name      string
    DeletedAt gorm.DeletedAt `gorm:"index"`
}

// 软删除
DB.Delete(&user, 1)

// 查询时会自动排除软删除记录
DB.Find(&users)

// 包含软删除记录
DB.Unscoped().Find(&users)

// 永久删除
DB.Unscoped().Delete(&user, 1)

4.3 物理删除 #

go
// 永久删除(绕过软删除)
DB.Unscoped().Delete(&user, 1)

五、原生SQL #

5.1 原生查询 #

go
// 原生SQL查询
var users []User
DB.Raw("SELECT * FROM users WHERE age > ?", 18).Scan(&users)

// 原生SQL查询单条
var user User
DB.Raw("SELECT * FROM users WHERE id = ?", 1).Scan(&user)

5.2 原生执行 #

go
// 执行原生SQL
DB.Exec("UPDATE users SET age = ? WHERE id = ?", 20, 1)

// 执行存储过程
DB.Exec("CALL update_user_age(?, ?)", 1, 20)

5.3 Rows #

go
// 使用Rows
rows, err := DB.Model(&User{}).Where("age > ?", 18).Rows()
if err != nil {
    panic(err)
}
defer rows.Close()

for rows.Next() {
    var user User
    DB.ScanRows(rows, &user)
    fmt.Println(user)
}

六、事务操作 #

6.1 自动事务 #

go
func CreateUserWithProfile(user *User, profile *Profile) error {
    return DB.Transaction(func(tx *gorm.DB) error {
        if err := tx.Create(user).Error; err != nil {
            return err
        }
        
        profile.UserID = user.ID
        if err := tx.Create(profile).Error; err != nil {
            return err
        }
        
        return nil
    })
}

6.2 手动事务 #

go
func CreateUserWithProfile(user *User, profile *Profile) error {
    tx := DB.Begin()
    defer func() {
        if r := recover(); r != nil {
            tx.Rollback()
        }
    }()
    
    if err := tx.Create(user).Error; err != nil {
        tx.Rollback()
        return err
    }
    
    profile.UserID = user.ID
    if err := tx.Create(profile).Error; err != nil {
        tx.Rollback()
        return err
    }
    
    return tx.Commit().Error
}

七、完整示例 #

7.1 用户CRUD #

go
package main

import (
    "net/http"
    "strconv"
    
    "github.com/gin-gonic/gin"
    "gorm.io/gorm"
)

type User struct {
    ID        uint           `gorm:"primaryKey" json:"id"`
    Name      string         `json:"name"`
    Email     string         `json:"email"`
    Age       int            `json:"age"`
    CreatedAt time.Time      `json:"created_at"`
    UpdatedAt time.Time      `json:"updated_at"`
    DeletedAt gorm.DeletedAt `gorm:"index" json:"-"`
}

func main() {
    r := gin.Default()
    
    // 列表查询
    r.GET("/users", func(c *gin.Context) {
        page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
        pageSize, _ := strconv.Atoi(c.DefaultQuery("page_size", "10"))
        
        var users []User
        var total int64
        
        DB.Model(&User{}).Count(&total)
        DB.Scopes(Paginate(page, pageSize)).Find(&users)
        
        c.JSON(200, gin.H{
            "data":  users,
            "total": total,
            "page":  page,
            "page_size": pageSize,
        })
    })
    
    // 获取详情
    r.GET("/users/:id", func(c *gin.Context) {
        id, _ := strconv.Atoi(c.Param("id"))
        
        var user User
        if err := DB.First(&user, id).Error; err != nil {
            if errors.Is(err, gorm.ErrRecordNotFound) {
                c.JSON(404, gin.H{"error": "用户不存在"})
                return
            }
            c.JSON(500, gin.H{"error": err.Error()})
            return
        }
        
        c.JSON(200, user)
    })
    
    // 创建用户
    r.POST("/users", func(c *gin.Context) {
        var user User
        if err := c.ShouldBindJSON(&user); err != nil {
            c.JSON(400, gin.H{"error": err.Error()})
            return
        }
        
        if err := DB.Create(&user).Error; err != nil {
            c.JSON(500, gin.H{"error": err.Error()})
            return
        }
        
        c.JSON(201, user)
    })
    
    // 更新用户
    r.PUT("/users/:id", func(c *gin.Context) {
        id, _ := strconv.Atoi(c.Param("id"))
        
        var user User
        if err := DB.First(&user, id).Error; err != nil {
            c.JSON(404, gin.H{"error": "用户不存在"})
            return
        }
        
        if err := c.ShouldBindJSON(&user); err != nil {
            c.JSON(400, gin.H{"error": err.Error()})
            return
        }
        
        if err := DB.Save(&user).Error; err != nil {
            c.JSON(500, gin.H{"error": err.Error()})
            return
        }
        
        c.JSON(200, user)
    })
    
    // 删除用户
    r.DELETE("/users/:id", func(c *gin.Context) {
        id, _ := strconv.Atoi(c.Param("id"))
        
        if err := DB.Delete(&User{}, id).Error; err != nil {
            c.JSON(500, gin.H{"error": err.Error()})
            return
        }
        
        c.Status(204)
    })
    
    r.Run()
}

func Paginate(page, pageSize int) func(db *gorm.DB) *gorm.DB {
    return func(db *gorm.DB) *gorm.DB {
        if page <= 0 {
            page = 1
        }
        if pageSize <= 0 {
            pageSize = 10
        }
        offset := (page - 1) * pageSize
        return db.Offset(offset).Limit(pageSize)
    }
}

八、总结 #

8.1 核心要点 #

操作 方法
创建 Create、CreateInBatches
查询 Find、First、Take、Last
更新 Update、Updates、Save
删除 Delete、Unscoped().Delete

8.2 最佳实践 #

实践 说明
错误处理 检查Error
软删除 使用DeletedAt
分页查询 使用Limit和Offset
事务 保证数据一致性

8.3 下一步 #

现在你已经掌握了CRUD操作,接下来让我们学习 数据库迁移,了解数据库结构管理!

最后更新:2026-03-28