GORM 单表操作与高级查询
GORM 单表操作与高级查询
1. GORM 简介与快速开始
GORM 是 Go 语言中最流行的 ORM 框架,它提供了丰富的功能:模型定义、自动迁移、链式操作、事务、预编译等。本文聚焦于单表操作,将带你从创建连接开始,逐步深入各种查询技巧,让你能高效地写出健壮的数据访问代码。
环境准备
在你的 Go 项目中安装 GORM 及对应的数据库驱动,这里以 MySQL 为例:
go get-ugorm.io/gorm go get-ugorm.io/driver/mysql2. 模型定义与数据库连接
假设我们有一个用户表users,对应结构体如下:
packagemainimport("gorm.io/gorm""gorm.io/driver/mysql""time")typeUserstruct{IDuint`gorm:"primaryKey"`Namestring`gorm:"size:100;not null"`Emailstring`gorm:"uniqueIndex;size:150"`Ageuint8IsActivebool`gorm:"default:true"`CreatedAt time.Time UpdatedAt time.Time}数据库连接与自动迁移:
funcmain(){dsn:="user:pass@tcp(127.0.0.1:3306)/mydb?charset=utf8mb4&parseTime=True&loc=Local"db,err:=gorm.Open(mysql.Open(dsn),&gorm.Config{})iferr!=nil{panic("failed to connect database")}// 自动迁移(仅开发环境推荐)db.AutoMigrate(&User{})}注意:生产环境建议使用版本化迁移工具,如
golang-migrate。
3. 单表基础操作:增删改查
3.1 创建记录
user:=User{Name:"Alice",Email:"alice@example.com",Age:25}result:=db.Create(&user)// 通过指针传递,ID 会自动回填fmt.Println(user.ID,result.RowsAffected,result.Error)批量创建:
users:=[]User{{Name:"Bob",Email:"bob@example.com",Age:30},{Name:"Charlie",Email:"charlie@example.com",Age:22},}db.Create(&users)3.2 查询单条记录
First:按主键升序获取第一条记录Take:没有排序,获取任意一条Last:按主键降序获取最后一条
varuser User db.First(&user,1)// 根据主键查找db.First(&user,"name = ?","Alice")// 条件查找// 检查是否未找到iferr:=db.Take(&user,"email = ?","no@exist.com").Error;err!=nil{iferrors.Is(err,gorm.ErrRecordNotFound){// 未找到记录}}3.3 查询多条记录
varusers[]User db.Find(&users)// 查询所有db.Where("age > ?",20).Find(&users)// 条件查询3.4 更新记录
// 更新单个字段db.Model(&user).Update("is_active",false)// 更新多个字段(使用 map 或 struct)db.Model(&user).Updates(User{Name:"Alice Updated",Age:26})db.Model(&user).Updates(map[string]interface{}{"name":"Alice Updated","age":26})// 批量条件更新db.Model(&User{}).Where("age < ?",25).Update("is_active",false)3.5 删除记录
// 软删除(如果模型包含 gorm.DeletedAt 字段,则会自动软删除)db.Delete(&user)// 根据主键删除db.Where("age > ?",60).Delete(&User{})// 批量删除4. 高级查询技巧
以下所有查询均基于单表(users)进行,所有示例可直接运行。
4.1 条件构建:Where / Or / Not
varusers[]User// 等值查询db.Where("name = ?","Alice").Find(&users)// 多个条件之间是 ANDdb.Where("name = ? AND age >= ?","Alice",20).Find(&users)// 使用 map 构建条件(等值 AND)db.Where(map[string]interface{}{"name":"Alice","is_active":true}).Find(&users)// 使用 struct 构建条件(零值字段会被忽略)db.Where(&User{Name:"Alice",Age:25}).Find(&users)// Or 条件db.Where("name = ?","Alice").Or("name = ?","Bob").Find(&users)// Not 条件db.Not("age = ?",18).Find(&users)db.Not("name",[]string{"Alice","Bob"}).Find(&users)// NOT IN4.2 比较与范围查询
// 大于、小于、不等于db.Where("age > ?",25).Find(&users)db.Where("age <> ?",30).Find(&users)// BETWEENdb.Where("age BETWEEN ? AND ?",20,30).Find(&users)// INdb.Where("name IN ?",[]string{"Alice","Bob"}).Find(&users)// 组合条件db.Where("(age > ? AND is_active = ?) OR email = ?",25,true,"admin@example.com").Find(&users)4.3 模糊匹配与正则
// LIKEdb.Where("name LIKE ?","%li%").Find(&users)// 包含 "li"// 正则(MySQL 示例)db.Where("name REGEXP ?","^A[a-z]+").Find(&users)// 以 A 开头,后面跟小写字母4.4 排序、分页与限量
// 排序db.Order("age desc, name asc").Find(&users)// 分页(Limit + Offset)varpageint=2varpageSizeint=10db.Limit(pageSize).Offset((page-1)*pageSize).Find(&users)// 更推荐使用 Scopes 封装分页逻辑funcPaginate(page,pageSizeint)func(db*gorm.DB)*gorm.DB{returnfunc(db*gorm.DB)*gorm.DB{ifpage<=0{page=1}ifpageSize<=0{pageSize=10}offset:=(page-1)*pageSizereturndb.Offset(offset).Limit(pageSize)}}db.Scopes(Paginate(2,10)).Find(&users)4.5 聚合查询:Count / Sum / Group By / Having
// 计数varcountint64db.Model(&User{}).Where("age > ?",20).Count(&count)// 分组统计每年龄的人数typeAgeGroupstruct{Ageuint8Countint}varageGroups[]AgeGroup db.Model(&User{}).Select("age, count(*) as count").Group("age").Find(&ageGroups)// 分组后过滤db.Model(&User{}).Select("age, count(*) as count").Group("age").Having("count > ?",2).Find(&ageGroups)// 求和、平均值等varsumAgeint64db.Model(&User{}).Select("sum(age)").Scan(&sumAge)4.6 字段选择与 Distinct
// 只查询特定字段db.Select("name","email").Find(&users)// 去重varemails[]stringdb.Model(&User{}).Distinct("email").Find(&emails)4.7 子查询与原生 SQL
单表操作也常需要子查询(如查询年龄大于平均年龄的用户):
varusers[]User subQuery:=db.Model(&User{}).Select("avg(age)")db.Where("age > (?)",subQuery).Find(&users)执行原生 SQL:
varuser User db.Raw("SELECT * FROM users WHERE name = ?","Alice").Scan(&user)// 使用 Exec 执行更新/删除db.Exec("UPDATE users SET is_active = ? WHERE age < ?",false,20)4.8 事务中的单表操作
err:=db.Transaction(func(tx*gorm.DB)error{// 在事务中执行一系列单表操作iferr:=tx.Create(&User{Name:"TxUser"}).Error;err!=nil{returnerr}iferr:=tx.Model(&User{}).Where("name = ?","Alice").Update("age",28).Error;err!=nil{returnerr}// 返回 nil 提交,返回 error 则回滚returnnil})iferr!=nil{// 处理事务失败}5. 实战案例:用户管理系统查询模块
下面给出一个完整的服务层示例,展示如何在函数中组合各种高级查询。
packageserviceimport("gorm.io/gorm""your-project/model")typeUserQuerystruct{KeywordstringAgeMinintAgeMaxintIsActive*bool// 使用指针以便区分零值PageintPageSizeint}funcSearchUsers(db*gorm.DB,q UserQuery)([]model.User,int64,error){varusers[]model.Uservartotalint64query:=db.Model(&model.User{})ifq.Keyword!=""{like:="%"+q.Keyword+"%"query=query.Where("name LIKE ? OR email LIKE ?",like,like)}ifq.AgeMin>0{query=query.Where("age >= ?",q.AgeMin)}ifq.AgeMax>0{query=query.Where("age <= ?",q.AgeMax)}ifq.IsActive!=nil{query=query.Where("is_active = ?",*q.IsActive)}// 先统计总数iferr:=query.Count(&total).Error;err!=nil{returnnil,0,err}// 分页与排序err:=query.Order("created_at DESC").Offset((q.Page-1)*q.PageSize).Limit(q.PageSize).Find(&users).Errorreturnusers,total,err}这样就能灵活应对前端的各种组合查询需求,并且实现了分页与总数返回。
