Skip to content

第42章:GORM 面试宝典与总复习 ​

本章目标 ​

学完本章后,你将能够:

  1. 系统回顾 GORM 所有知识点
  2. 自信回答 GORM 相关面试题
  3. 根据场景选择合适的技术方案

42.1 知识体系回顾 ​

GORM v2 知识体系
├── 基础概念
│   ├── ORM 思想:struct ↔ 表
│   ├── 约定优于配置
│   └── 链式调用(Chain Method vs Finisher Method)
├── 模型定义(Phase 2)
│   ├── gorm.Model(ID/CreatedAt/UpdatedAt/DeletedAt)
│   ├── 字段标签(column/type/size/default/not null/index 等)
│   ├── 命名约定(表名复数蛇形、列名蛇形)
│   ├── AutoMigrate(只做加法不做减法)
│   └── Scanner/Valuer 接口
├── CRUD(Phase 3)
│   ├── Create/批量/UPSERT/FirstOrCreate
│   ├── First/Take/Last/Find(找不到行为差异)
│   ├── Where(字符串/结构体/Map)零值陷阱
│   ├── Update/Save/UpdateColumn 的选择
│   └── Delete/软删除/Unscoped
├── 高级查询(Phase 4)
│   ├── Order/Limit/Offset/分页
│   ├── Select/Group/Having/聚合
│   ├── Joins/InnerJoins
│   ├── 子查询/Raw/Exec/Scan
│   └── Scopes(可复用查询片段)
├── 关联关系(Phase 5)
│   ├── BelongsTo:外键在自己
│   ├── HasOne:外键在对方 + UNIQUE
│   ├── HasMany:外键在对方
│   ├── Many2Many:中间表
│   ├── Preload:解决 N+1(2条SQL)
│   └── Association 模式
├── 事务与钩子(Phase 6)
│   ├── Transaction(func){}/手动事务
│   ├── 悲观锁(FOR UPDATE)/乐观锁(版本号)
│   ├── 钩子生命周期
│   └── 密码加密/UUID 生成
└── 高级特性(Phase 7-8)
    ├── Gen 代码生成器
    ├── DBResolver(读写分离)
    ├── 连接池配置
    ├── SQL 注入防护
    └── 测试(SQLite :memory:)

42.2 高频面试题 30 题 ​

基础题 ​

Q1:GORM 零值陷阱是什么?如何避免?

A:GORM 在 Updates 和 Where 中会跳过零值字段(int 的 0、string 的 ""、bool 的 false)。解决方案:①用指针类型;②用 Map;③用 Select 强制指定字段。

Q2:Save vs Update vs Updates 的区别?

A:Save 全字段保存(根据主键判断 Insert/Update);Update 更新单列;Updates 更新多列(struct 零值跳过,map 不跳过)。日常推荐 Update/Updates,Save 有覆盖风险。

Q3:First vs Take vs Last vs Find 的区别?

A:First 按主键升序第一条;Take 随机一条;Last 按主键降序最后一条;Find 取所有。找不到时前三者返回 ErrRecordNotFound,Find 返回空切片不报错。

Q4:软删除原理?如何查询已删除记录?

A:软删除设置 deleted_at 时间戳而非真正 DELETE。普通查询自动过滤(WHERE deleted_at IS NULL)。Unscoped() 查询所有(包括已删除)。Unscoped().Delete() 硬删除。

Q5:AutoMigrate 能做什么、不能做什么?

A:能创建表、添加列、添加索引。不能删除列、修改列名、删除索引。设计原则:只做加法不做减法,保护数据安全。

查询题 ​

Q6:N+1 问题是什么?如何解决?

A:查询 N 条主记录后,循环中逐条查关联 → N+1 条 SQL。解决方案:Preload(2条SQL)或 Joins(1条SQL)。

Q7:Preload 和 Joins 的区别?

A:Preload 用多条 SQL 加载关联,自动填充结构体,适合纯展示。Joins 用一条 SQL(JOIN),适合需要关联条件过滤的场景。一对多/多对多必须用 Preload。

Q8:如何实现分页查询?

A:db.Limit(pageSize).Offset((page-1)*pageSize).Find() + db.Count(&total)。大偏移量(翻页太深)可用游标分页(WHERE id > lastID)。

Q9:Where(struct) 和 Where(map) 有什么区别?

A:struct 零值被跳过(int 的 0、string 的 ""),map 不跳过。查询包含零值的条件用 map 或字符串条件。

Q10:如何防止 SQL 注入?

A:始终使用 ? 占位符(参数化查询),不拼接字符串。GORM 的 Where/Find/Create 等方法默认安全。Raw/Exec 也要用占位符。ORDER BY 等无法占位的参数要白名单校验。

关联题 ​

Q11:HasOne vs BelongsTo 选哪个?

A:谁"归属"于对方 → BelongsTo(外键在自己);谁"拥有"对方 → HasOne(外键在对方)。User BelongsTo Company(外键 company_id 在 users),User HasOne CreditCard(外键 user_id 在 credit_cards)。

Q12:多对多中间表有额外字段怎么办?

A:手动定义中间表 Model + SetupJoinTable,然后直接操作中间表。

Q13:Association 模式的常用操作?

A:Append(追加)、Delete(删除关联)、Replace(替换)、Clear(清空)、Count(统计)。注意 Delete 只解除关联不删除记录。

事务题 ​

Q14:如何正确使用事务?

A:推荐 db.Transaction(func(tx *gorm.DB) error { ... return nil }),return nil 提交,return error 回滚。闭包内必须用 tx 不能用 db。

Q15:悲观锁 vs 乐观锁?

A:悲观锁(SELECT FOR UPDATE)锁住行,适合冲突多的场景(如库存扣减)。乐观锁(版本号 + RowsAffected 判断)不锁行,适合冲突少、读多写少的场景。

性能题 ​

Q16:如何优化批量插入?

A:CreateInBatches 分批插入 + 事务包裹 + SkipDefaultTransaction + PrepareStmt。

Q17:连接池怎么配置?

A:MaxOpenConns(25-100)、MaxIdleConns(~10)、ConnMaxLifetime(3-5min)、ConnMaxIdleTime(1min)。生产必须配置上限,避免连接数爆炸。


42.3 场景设计题 ​

场景1:设计博客系统 GORM 模型

User (HasMany Articles, HasMany Comments), Article (BelongsTo User, HasMany Comments, Many2Many Tags), Comment (BelongsTo User, BelongsTo Article), Tag (Many2Many Articles)

场景2:转账操作的事务实现

事务中:A 扣钱 → B 加钱 → 记录日志。任何一步失败 → 全部回滚。加悲观锁防并发。

场景3:千万级数据的分页优化

OFFSET 过大 → 用游标分页(WHERE id > lastID LIMIT 10),或子查询定位 + 覆盖索引。


42.4 选型指南 ​

方案适用场景
GORM大多数 Go Web 项目,功能全、社区大
sqlx喜欢手写 SQL,只需要薄封装
sqlc强 SQL 能力团队,需要极高类型安全
ent大型项目,需要 Schema 驱动的代码生成

42.5 进阶学习路线 ​

  1. 阅读 GORM 源码(gorm.Open → *gorm.DB → Callback → Clause)
  2. 学习 Go database/sql 标准库
  3. 对比学习 ent、sqlc
  4. 参与 GORM 开源贡献(Good First Issue)
  5. 在实际项目中深度使用 GORM

最后的话 ​

你从零开始,走完了 42 章的学习旅程。你掌握了:

  • GORM 的模型定义、CRUD、查询、关联、事务、钩子
  • 性能优化、安全防护、测试方法
  • 三个完整的实战项目
  • 30 道面试题的答案

GORM 只是一个工具,真正重要的是你对数据库和业务的理解。工具帮你少写模板代码,但思考和分析永远是你自己的。

时刻保持好奇心,永不停下学习的脚步。祝你成为一名优秀的后端工程师!


练习题 ​

  1. 默写 GORM 知识体系树(参考 42.1)。
  2. 从 30 道面试题中随机抽 10 题,不看答案独立回答。
  3. 找一个实际项目(或自己设计一个),用 GORM 从头实现数据库层。
  4. 阅读 GORM 官方文档中你还没看过的部分。

把零散知识,整理成可以反复走的路。