反模式
贫血模型、上帝 Entity、过度 ORM。
25 分钟
反模式
ORM 能提效,也能把项目拖进 贫血模型、上帝 Entity、隐式 SQL 风暴。识别反模式比背注解更重要。
学习目标
- 列举常见 ORM 反模式及后果
- 对照 CodeRoadMap 域模型应如何划分
- 知道何时退回 SQL / MyBatis
贫血模型
Entity 只有 getter/setter,业务全堆在「Service 上帝类」:
// 反模式:Track 无行为,TrackService 3000 行
track.setStatus("published");
lesson.setStatus("published");
progressRepository.deleteByTrackId(id);
改进:适度 富领域模型——简单规则放 Entity(如 lesson.markPublished()),跨聚合用 Domain Service + @Transactional。
上帝 Entity
一张表 50 个字段、10 种 @OneToMany,任何接口都 findById 拉全图:
@Entity
public class Track {
// cf_track 字段 + 冗余统计 + 临时 DTO 字段 ...
@OneToMany private List<Lesson> lessons;
@OneToMany private List<Progress> progresses;
@OneToMany private List<Comment> comments; // 列表页全加载
}
改进:读模型 DTO、分页、专用查询(见 orm-nplus1);cf_track 与 cf_progress 不必每次绑定加载。
过度 ORM
| 反模式 | 后果 |
|---|---|
| 全局 EAGER | 列表接口 SQL 爆炸 |
循环里 save() | N 次 INSERT |
| JPQL 拼字符串动态 SQL | 难维护,不如 MyBatis |
ddl-auto=update 上生产 | 不可控 DDL |
报表、复杂聚合:MyBatis 或原生 SQL(orm-mybatis),别硬写 HQL。
双轨模型打架
同一 cf_lesson 既用 JPA Entity 又用 MyBatis DTO,字段名不一致——改表漏改一半。
约定:每表一个权威域对象;MyBatis resultMap 映射到同一 POJO 或明确只读 DTO。
忽视 SQL 与索引
ORM 生成 SELECT *、缺索引的 WHERE——本地数据少不觉,上线全表扫。
养成看 慢查询日志 的习惯,回到 sql-index。
相关课程
- orm-nplus1
- sql-design
- mongo-vs-sql — 别用错存储