用 Go 之后一直拿 GORM 当主力 ORM,两年下来项目里大大小小的查询都过过手,这篇是阶段性的反思,有一些想法可能和主流意见不一致。
ORM 用得最爽的地方不是 CRUD,是迁移和事务。 AutoMigrate 在开发阶段确实是生产力,事务的 callback 风格也比手写 begin/commit/rollback 干净。这两块我没打算退回裸写 SQL。
但我现在很后悔的几件事:
- 早期放任了链式调用的滥用。 业务代码里到处是
db.Where().Where().Joins().Preload()长链,SQL 长什么样全靠脑补。有一次一个 Preload 链带出来三千行数据,接口直接超时。现在我们要求超过两个条件的查询一律下沉到 SQL 文件(用 sqlc 管理),ORM 只处理简单的单表操作。 First和Take的区别踩过坑。First按主键排序,表大了以后 where 条件没走索引的话性能很难看。我的原则变成了:能确定唯一性的用Take,别让 ORM 替你排序。- hook 里有业务逻辑是灾难。 前任同事在
AfterUpdate里发了 MQ 消息,某天这个逻辑挂了导致所有用户更新接口静默失败,排查了一晚上才找到。现在我们规定:GORM 的 hook 里只允许写与数据库直接相关的逻辑,业务副作用一律上移到 service 层。
还没想清楚的: 要不要全面转向 sqlx 或 sqlc。团队里年轻的同事喜欢 ORM 的省事,老一些的觉得 SQL 可见性更重要。目前的项目状态是"两者混用",边界靠 code review 人的自觉维持——这不健康,但也没烂到需要立刻改的程度。
技术选型这种事,最诚实的答案往往是"当前规模下都行"。真正的问题从来不是工具,是什么时候该承认现在的用法已经不对了。