当前位置: 首页 > news >正文

GORM 单表操作与高级查询

GORM 单表操作与高级查询


1. GORM 简介与快速开始

GORM 是 Go 语言中最流行的 ORM 框架,它提供了丰富的功能:模型定义、自动迁移、链式操作、事务、预编译等。本文聚焦于单表操作,将带你从创建连接开始,逐步深入各种查询技巧,让你能高效地写出健壮的数据访问代码。

环境准备

在你的 Go 项目中安装 GORM 及对应的数据库驱动,这里以 MySQL 为例:

go get-ugorm.io/gorm go get-ugorm.io/driver/mysql

2. 模型定义与数据库连接

假设我们有一个用户表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 IN

4.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}

这样就能灵活应对前端的各种组合查询需求,并且实现了分页与总数返回。

http://www.gsyq.cn/news/1637266.html

相关文章:

  • 哪怕MCP再强,我也劝你保留一点“控制欲”
  • Python异步代理池实战:从requests阻塞到httpx.AsyncClient,爬虫效率翻倍的踩坑记录
  • Flexbox对齐搞错,布局全崩!
  • 2026智能床垫的技术架构:从传感器到AI算法的完整链路
  • Qwen3.6-27B 本地代码能力评测(一)
  • 解密微信QQ防撤回:Windows平台逆向工程实战指南 [特殊字符]️
  • 【Springboot毕设全套源码+文档】基于springboot电子外设销售系统的设计与实现(丰富项目+远程调试+讲解+定制)
  • 【面板数据模型实战】从理论到Stata/R/Python实现与选择
  • 如何高效使用RoboCopy GUI工具:从命令行到图形化的完整实战指南
  • 基于51单片机的智能热水器温度水温测量控制系统电子套件定制13(设计源文件+万字报告+讲解)(支持资料、图片参考_相关定制)_文章底部可以扫码
  • 类型分类、联合类型、交叉类型
  • 2026软件测试面试官在面试的时候会做些什么?
  • DDR3 T型拓扑 PCB 设计实战:4片 MT41J256M8HX-15E 布局与端接电阻配置
  • Supabase 数据库介绍:开源 Firebase 替代方案
  • 企业知识库更新闭环:RAG 不是接入一次就结束
  • 如何完整备份微信聊天记录:WeChatMsg数据自主管理实用指南
  • PADS VX2.8 BGA扇出实战:1.0mm间距芯片的4步配置与十字通道预留
  • Linux 服务器访问控制:组合使用 PAM wheel 组与 iptables 限制 SSH 来源
  • 2026年联发科嵌入式岗位高频面试题带参考答案
  • 数据集切分策略:随机划分不一定适合时间序列任务
  • 3个关键步骤让AirPods在Windows上重获完整功能:AirPodsDesktop终极解决方案
  • 软件测试面试总结分享
  • 计算机网络知识点总结(四)Linux C++ Socket实现“伪”半双工聊天室程序
  • 大疆 M3508 电机速度 PID 调参实战:从振荡到稳定,3 组参数对比分析
  • 【全网大测评】有没有降AI率的靠谱软件推荐?2026年亲测15款降AI率工具,帮你避坑省钱!
  • Go 微服务限流:别把所有请求都堵在入口
  • 2026建筑合同管理系统怎么选才不踩坑:房建企业合同、签证、产值与付款闭环指南
  • AI 创业假设验证:先证明有人痛,再证明模型强
  • 2026年暑假学习规划排名:这样安排让孩子高效又充实
  • 椭偏仪—介质膜的首选方法