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/19 15:31:34 阅读更多 →
EasyAdmin 菜单管理完全指南:无限极菜单与权限节点的联动配置

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

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

2026/10/25 3:25:58 阅读更多 →
地图颜色自动匹配: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/26 15:28:50 阅读更多 →

最新新闻

Electron桌面应用开发实战:从环境搭建到打包发布全流程

Electron桌面应用开发实战:从环境搭建到打包发布全流程

做前端这些年,我大部分时间都在跟浏览器标签页打交道,直到第一次把桌面应用跑起来,才发现原来自己一直被限制在地址栏和刷新按钮围起来的框框里。Electron这个词,最开始是在某段时间频繁出现在同事的讨论里,一个用HTML…

2026/10/26 12:00:21 阅读更多 →
滑盖智能锁T109深度解析:结构原理、安装调试与故障排查

滑盖智能锁T109深度解析:结构原理、安装调试与故障排查

简介:亚太天能T109滑盖智能锁产品说明书是一份PDF格式的官方技术文档,主要面向智能锁安装人员、终端用户及售后维护者,帮助快速掌握产品安装、配置与常见故障处理。全包仅1个PDF文件,大小4.37MB,内容结构清晰&#xff…

2026/10/25 22:36:10 阅读更多 →
PMSM弱磁控制原理与Simulink建模:高速AGV驱动系统设计指南

PMSM弱磁控制原理与Simulink建模:高速AGV驱动系统设计指南

上个月我在调试一台高速AGV的驱动系统,整车要求空载跑到8m/s,电机转速已经拉到额定点,驱动器的输出电压也顶到了母线电压极限附近。再往上踩速度指令,转速纹丝不动,三相电流反而开始震荡。那会儿我的第一反应就是&…

2026/10/26 4:52:12 阅读更多 →
C++核心知识点:从内存管理到STL的实战指南

C++核心知识点:从内存管理到STL的实战指南

C 这门语言我前前后后折腾了十几年,用它写过嵌入式驱动、做过游戏服务端,也搭过基础组件库。每次看到新人一头扎进"C 核心知识点"这个搜索词,我其实挺感慨的——这门语言的难点从来不是语法多,而是知识点之间的耦合太深…

2026/10/26 12:00:21 阅读更多 →
T109滑盖智能锁怎么装?安装调试与故障排查全攻略

T109滑盖智能锁怎么装?安装调试与故障排查全攻略

简介:亚太天能T109滑盖智能锁产品说明书以PDF格式发布,适合智能锁用户、安装人员与售后维修人员阅读。内容围绕T109系列指纹密码刷卡锁展开,先介绍前面板、后面板、锁体、机械钥匙、电池盒盖等部件名称与功能,再给出完整安装步骤&…

2026/10/26 5:00:37 阅读更多 →
LLM知识降熵:从信息垄断到理解权下放

LLM知识降熵:从信息垄断到理解权下放

你有没有过这种经历:翻开源码注释、论文摘要或者一份专业报告,每个字都认识,连在一起却像天书。想找人聊聊,身边要么没人懂,要么懂的懒得跟你解释。后来我试着把同一段内容丢给一个大语言模型,几秒钟之后&a…

2026/10/26 3:16:46 阅读更多 →

日新闻

周新闻

月新闻

我发现了一个新思路:用 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/26 12:00:19 阅读更多 →
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/25 13:21: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/26 8:34:37 阅读更多 →