主题
17 · GORM 与 MySQL
目标:用 GORM 完成模型、迁移、CRUD,理解与 Mongo/Mongoose 的差异。
前置:16 · JWT · 13 · Viper
文档:GORM · Go MySQL Driver
对照:Mongoose / Prisma;Journal 现用 Mongo → 这里练 关系型心智
1. 为何本系列选 MySQL
| 点 | 说明 |
|---|---|
| 招聘与课设 | MySQL + ORM 极常见 |
| 与 Mongo 对照 | 强制想清表结构、索引、事务 |
| Journal | 生产可继续 Mongo;练手仓用 MySQL 不冲突 |
2. 最小可跑
bash
# 本地需 MySQL;或等 22 篇 Compose
go get gorm.io/gorm gorm.io/driver/mysql1
2
2
go
type User struct {
ID uint `gorm:"primaryKey"`
Email string `gorm:"size:191;uniqueIndex;not null"`
Password string `gorm:"size:255;not null"` // 存 hash
CreatedAt time.Time
UpdatedAt time.Time
DeletedAt gorm.DeletedAt `gorm:"index"`
}
type Note struct {
ID uint `gorm:"primaryKey"`
UserID uint `gorm:"index;not null"`
Content string `gorm:"type:text;not null"`
CreatedAt time.Time
UpdatedAt time.Time
}
func OpenDB(dsn string) (*gorm.DB, error) {
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
if err != nil {
return nil, err
}
if err := db.AutoMigrate(&User{}, &Note{}); err != nil {
return nil, err
}
return db, nil
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
CRUD 片段:
go
db.Create(¬e)
db.Where("user_id = ?", uid).Order("id desc").Limit(20).Find(¬es)
db.First(¬e, id)
db.Model(¬e).Update("content", content)
db.Delete(¬e) // 软删若有 DeletedAt1
2
3
4
5
2
3
4
5
3. 与 Mongoose 对照
| Mongoose | GORM |
|---|---|
| Schema / Model | struct + tag |
findById | First(&m, id) |
| populate | Preload / Joins |
| 事务 | db.Transaction(func(tx *gorm.DB) error {...}) |
4. 动手清单
- [ ] User + Note 两表,Note 归属 User
- [ ] 创建笔记时写
user_id(来自 JWT,不来自 body) - [ ] 列表只查当前用户
- [ ] 密码用 bcrypt,不存明文
- [ ] 慢查询意识:为
user_id建索引(tag 已示范)
5. 项目驱动
| 场景 | 表设计直觉 |
|---|---|
| 手帐 | notes + 可选 tags 关联表(入门可 JSON 列,进阶再范式化) |
| 登录 | users.email 唯一索引 |
| 以后缓存 | 热点 note 用 Redis,源仍在 MySQL(19~21) |
6. 常见坑 + AI 审查
| 坑 | 说明 |
|---|---|
| AutoMigrate 当唯一迁移方案上生产 | 入门可以;生产要迁移工具/评审 |
| SQL 注入 | 用 Where("id = ?", id),勿拼字符串 |
| N+1 | 循环里查关联;用 Preload |
AI 忽略 parseTime=true | DSN 缺了时间解析会怪 |
