GORM入门:ORM映射与CRUD
GORM入门:ORM映射与CRUD摘要: 本篇从GORM的安装和连接讲起演示模型定义与表结构映射、AutoMigrate自动建表、完整的CRUD操作和条件查询技巧讲解软删除机制和Unscoped用法分享软删除导致唯一索引冲突的踩坑经历对比database/sql、sqlx和GORM三种数据访问方式的开发效率。开篇故事上一篇我们用database/sql写了一套CRUD每个查询都要手动Scan字段加一个字段就得改SQL和Scan两处。我有个项目用户表改了三次结构每次改完都要全局搜索所有涉及User的SQL和Scan生怕漏一个。后来我把这个项目迁移到GORM。定义好structGORM自动处理字段映射加字段只需要改struct和调一次AutoMigrate。开发效率提升非常明显原来一个接口要写30分钟SQL和绑定代码迁移后10分钟搞定。当然GORM也有它的坑。这篇我们从入门开始把CRUD和模型映射讲透坑留到后面说。一、安装与连接packagemainimport(logtimegorm.io/driver/mysqlgorm.io/gorm)funcmain(){// DSN与database/sql的格式一致dsn:root:123456tcp(127.0.0.1:3306)/testdb?charsetutf8mb4parseTimetruelocLocal// gorm.Open建立连接并初始化// 第二个参数是配置项后面性能篇会详细讲db,err:gorm.Open(mysql.Open(dsn),gorm.Config{})iferr!nil{log.Fatal(连接数据库失败:,err)}// 获取底层sql.DB配置连接池// GORM底层仍然使用database/sqlsqlDB,_:db.DB()sqlDB.SetMaxOpenConns(25)// 最大连接数sqlDB.SetMaxIdleConns(10)// 最大空闲连接数sqlDB.SetConnMaxLifetime(5*time.Minute)// 连接最大存活时间log.Println(GORM连接成功)}GORM底层仍然使用database/sql所以连接池配置方式完全一样。gorm.Open返回的是*gorm.DB通过db.DB()可以拿到底层的*sql.DB。二、模型定义与自动建表GORM用struct tag来映射数据库表结构。import(timegorm.io/gorm)// User 用户模型// GORM约定struct名为User表名为userstypeUserstruct{// ID是GORM的默认主键// 类型为uint对应数据库BIGINT UNSIGNEDIDuintgorm:primaryKey// size控制VARCHAR长度not null非空约束Namestringgorm:size:50;not null// uniqueIndex创建唯一索引Emailstringgorm:size:100;uniqueIndex// default设置默认值Ageintgorm:default:18Statusstringgorm:size:20;default:active// CreatedAt和UpdatedAt自动管理CreatedAt time.Time UpdatedAt time.Time// DeletedAt软删除字段// 有值表示已删除查询时自动过滤DeletedAt gorm.DeletedAtgorm:index}AutoMigrate会根据struct创建或更新表结构。// AutoMigrate自动建表// 已存在的表会追加缺失的列和索引// 不会删除已有的列也不会修改列类型db.AutoMigrate(User{})// 生成的SQL大概是这样// CREATE TABLE users (// id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,// name VARCHAR(50) NOT NULL,// email VARCHAR(100),// age BIGINT DEFAULT 18,// ...// PRIMARY KEY (id)// )注意AutoMigrate是追加式的只加不删。如果你删掉struct里的某个字段数据库表里对应的列还在。生产环境不建议用AutoMigrate管理表结构应该用migration工具比如golang-migrate做版本化迁移。三、CRUD操作// Create 插入记录funcCreate(db*gorm.DB){user:User{Name:张三,Email:zhangsanexample.com,Age:25,}// Create插入一条记录result:db.Create(user)// result.Error 错误信息// result.RowsAffected 受影响行数// user.ID 自动回填插入后的自增IDfmt.Println(新用户ID:,user.ID)}// 批量插入funcBatchCreate(db*gorm.DB){users:[]User{{Name:李四,Email:lisiexample.com,Age:30},{Name:王五,Email:wangwuexample.com,Age:28},}// 传入切片批量插入db.Create(users)// 每个元素的ID都会自动回填}// Read 查询操作funcRead(db*gorm.DB){varuser User// First按主键查询没找到返回ErrRecordNotFounddb.First(user,1)// SQL: SELECT * FROM users WHERE id 1 LIMIT 1// 按条件查询db.First(user,email ?,zhangsanexample.com)// 查询多条varusers[]User db.Find(users,age ?,20)// SQL: SELECT * FROM users WHERE age 20// 链式条件查询db.Where(name LIKE ?,%张%).Where(age ?,20).Order(age DESC).Limit(10).Offset(0).Find(users)// SQL: SELECT * FROM users// WHERE name LIKE %张% AND age 20// ORDER BY age DESC LIMIT 10 OFFSET 0}// Update 更新操作funcUpdate(db*gorm.DB){varuser User db.First(user,1)// Save保存所有字段user.Age26db.Save(user)// SQL: UPDATE users SET ... WHERE id 1// Update更新单个字段db.Model(user).Update(name,张三丰)// SQL: UPDATE users SET name 张三丰 WHERE id 1// Updates更新多个字段// 用map可以更新零值db.Model(user).Updates(map[string]interface{}{name:张三,age:27,})// SQL: UPDATE users SET name 张三, age 27 WHERE id 1}// Delete 删除操作funcDelete(db*gorm.DB){// 软删除设置deleted_at字段的值// 查询时GORM自动过滤已软删除的记录db.Delete(User{},1)// SQL: UPDATE users SET deleted_at NOW() WHERE id 1// Unscoped查询包含软删除的记录varusers[]User db.Unscoped().Find(users)// SQL: SELECT * FROM users (不带deleted_at过滤)// Unscoped永久删除db.Unscoped().Delete(User{},1)// SQL: DELETE FROM users WHERE id 1}四、条件查询技巧// 常用查询方式汇总funcQueryExamples(db*gorm.DB){varusers[]User// IN查询db.Where(id IN ?,[]int{1,2,3}).Find(users)// BETWEEN范围查询db.Where(age BETWEEN ? AND ?,20,30).Find(users)// OR条件db.Where(name ?,张三).Or(name ?,李四).Find(users)// Select指定字段减少网络传输量db.Select(id,name).Find(users)// SQL: SELECT id, name FROM users// Count计数varcountint64db.Model(User{}).Where(age ?,20).Count(count)// 分组与聚合查询typeResultstruct{AgeintTotalint}varresults[]Result db.Model(User{}).Select(age, count(*) as total).Group(age).Find(results)}五、独家踩坑:软删除的意外行为GORM默认开启软删除这给了我一个很大的惊喜。// 踩坑场景:唯一索引与软删除冲突typeUserstruct{IDuintEmailstringgorm:uniqueIndex// 唯一索引NamestringDeletedAt gorm.DeletedAtgorm:index// 软删除}// 用户注销后重新注册funcReRegister(db*gorm.DB,emailstring)error{// 先删除旧账号软删除db.Where(email ?,email).Delete(User{})// deleted_at被设值但记录还在表里// 再创建新账号user:User{Email:email,Name:新用户}err:db.Create(user).Error// 报错: Duplicate entry xxx for key idx_email// 唯一索引不关心deleted_at旧记录还在returnerr}用户注销后想用同一个邮箱重新注册结果唯一索引报重复。软删除只是把deleted_at设了值记录还在表里唯一索引照样拦截。这个问题有好几种解法。可以把唯一索引改成联合索引(email, deleted_at)但deleted_at是NULL时联合索引的行为在不同数据库有差异。也可以在业务层处理注销时把email加个后缀比如deleted_123xxx.com。最干净的方式是注销时判断是否已有软删除记录有就恢复更新而不是新建。软删除在大多数场景很方便但涉及唯一约束时要特别小心。六、对比分析特性database/sqlsqlxGORM开发效率低中高SQL控制力完全手动完全手动自动生成结构体映射手动Scan自动Scan自动映射软删除无无内置迁移工具无无AutoMigrate学习成本低低中从database/sql到GORM开发效率提升很大代价是失去了对SQL的完全控制。简单CRUD用GORM很爽复杂查询GORM的链式API有时候比直接写SQL还难读。下篇我们讲GORM进阶关联关系和事务那里GORM的优势和劣势都会更明显。总结GORM把重复的数据库操作代码抽象掉了模型定义即表结构CRUD一行搞定。记住两个要点struct tag控制映射规则软删除影响唯一索引。下一篇进入GORM进阶讲关联关系、钩子和事务。

相关新闻

AutoSub 静音检测算法解析:如何用 SVM 精准分割音频片段提升识别精度

AutoSub 静音检测算法解析:如何用 SVM 精准分割音频片段提升识别精度

AutoSub 静音检测算法解析:如何用 SVM 精准分割音频片段提升识别精度 【免费下载链接】AutoSub A CLI script to generate subtitle files (SRT/VTT/TXT) for any video using either DeepSpeech or Coqui 项目地址: https://gitcode.com/gh_mirrors/auto/AutoSub…

2026/10/4 15:53:21 阅读更多 →
EasyAdmin 菜单管理完全指南:无限极菜单与权限节点的联动配置

EasyAdmin 菜单管理完全指南:无限极菜单与权限节点的联动配置

EasyAdmin 菜单管理完全指南:无限极菜单与权限节点的联动配置 【免费下载链接】easyadmin 框架主要使用ThinkPHP6.0 layui,拥有完善的权限的管理模块以及敏捷的开发方式,让你开发起来更加的舒服。 项目地址: https://gitcode.com/gh_mirro…

2026/10/4 15:53:21 阅读更多 →
地图颜色自动匹配:map-vectorizer如何用PIL与最近邻算法还原建筑属性

地图颜色自动匹配:map-vectorizer如何用PIL与最近邻算法还原建筑属性

地图颜色自动匹配:map-vectorizer如何用PIL与最近邻算法还原建筑属性 【免费下载链接】map-vectorizer An open-source map vectorizer 项目地址: https://gitcode.com/gh_mirrors/ma/map-vectorizer 📌 文章概要 在历史地图矢量化工作中&#x…

2026/10/3 9:25:37 阅读更多 →

最新新闻

办公楼局域网设计:100个IP服务120台终端的DHCP与级联方案

办公楼局域网设计:100个IP服务120台终端的DHCP与级联方案

简介:面向计算机网络/数据通信课程设计的一体化办公楼局域网系统设计报告,以五层楼、120台终端为场景,针对仅100个可用IP的客观约束,给出基于DHCP的IP地址分配、Cisco WS-C2960-24交换机级联、星型拓扑结构、Windows NT网络操作系…

2026/10/4 15:52:21 阅读更多 →
ModbusTCP调试实战:从协议帧到上位机避坑指南

ModbusTCP调试实战:从协议帧到上位机避坑指南

1. 为什么ModbusTCP调试总在“最后一公里”翻车搞上位机开发的人,十个里有八个在ModbusTCP上栽过跟头。我做了十多年工控上位机,从早期的串口Modbus RTU到现在的以太网ModbusTCP,踩过的坑能写一本小册子。最让人抓狂的不是协议本身有多复杂&a…

2026/10/4 15:52:21 阅读更多 →
Spring Boot图片上传全流程拆解:本地存储、URL映射与避坑实践

Spring Boot图片上传全流程拆解:本地存储、URL映射与避坑实践

苍穹外卖这个项目做到Day3,我原本的计划是直接铺菜品管理的增删改查,结果刚动手就被一个“不起眼”的功能卡住了——菜品图片。前端要传图,后端得把文件接住、存到本地、再返回一个能访问的URL,整条链路不打通,后面菜品…

2026/10/4 15:52:20 阅读更多 →
基于SpringBoot+Vue3的动物领养管理平台实战:从数据库设计到前后端联调

基于SpringBoot+Vue3的动物领养管理平台实战:从数据库设计到前后端联调

最近有个做校园公益的朋友来找我,说流浪动物救助站缺一套能跑起来的领养管理系统,手动登记信息实在撑不住了。我干脆把一套自己维护的动物领养平台源码重新整理了一遍,基于 Java SpringBoot Vue3 MyBatis MySQL 的前后端分离方案&#xf…

2026/10/4 15:52:20 阅读更多 →
MATLAB复现红外弱小目标检测IPI算法:低秩稀疏原理与调参实战

MATLAB复现红外弱小目标检测IPI算法:低秩稀疏原理与调参实战

第一次用MATLAB复现红外弱小目标检测算法的人,多半会在看到真实红外图像的那一刻产生一种挫败感:目标在图像里肉眼几乎看不出来,背景里的云层边缘、地物轮廓却比目标亮得多。传统滤波方法在这种场景下要么把目标和噪声一起滤掉,要…

2026/10/4 15:52:20 阅读更多 →
【最新原创大数据】基于大数据的酒店数据分析与可视化,附源码_高质量项目_可视化_数据分析_毕设选题推荐_SPark_Hadoop_毕设指导

【最新原创大数据】基于大数据的酒店数据分析与可视化,附源码_高质量项目_可视化_数据分析_毕设选题推荐_SPark_Hadoop_毕设指导

💖💖作者:计算机毕业设计杰瑞 💙💙个人简介:曾长期从事计算机专业培训教学,本人也热爱上课教学,语言擅长Java、微信小程序、Python、Golang、安卓Android等,开发项目包括…

2026/10/4 15:51:19 阅读更多 →

日新闻

KT148A语音芯片外挂8002D功放的工程实践指南

KT148A语音芯片外挂8002D功放的工程实践指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/4 1:00:58 阅读更多 →
LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/4 1:00:58 阅读更多 →
ARM架构深度解析:从RISC设计理念到交叉编译实战

ARM架构深度解析:从RISC设计理念到交叉编译实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/4 1:00:58 阅读更多 →

周新闻

KT148A语音芯片外挂8002D功放的工程实践指南

KT148A语音芯片外挂8002D功放的工程实践指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/4 1:00:58 阅读更多 →
LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/4 1:00:58 阅读更多 →
ARM架构深度解析:从RISC设计理念到交叉编译实战

ARM架构深度解析:从RISC设计理念到交叉编译实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/4 1:00:58 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/4 11:40:45 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/4 9:43:54 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/3 9:42:36 阅读更多 →