Java 后端
MyBatis 与 JPA:持久层选型不该是一场站队
从设备列表、维修详情和运营报表三种查询形态出发,建立 ORM 与 SQL 控制力的取舍框架。
核心洞察:持久层的关键问题不是“哪个更先进”,而是对象模型、查询复杂度、团队能力与演进成本如何匹配。 霓虹港有三类数据访问:工单写入需要保持聚合一致性;设备详情要沿关联读取;运营看板需要复杂聚合、窗口函数与多维筛选。试图用一种工具解决全部问题,通常会让其中至少一类变得难以维护。
JPA 擅长表达对象生命周期
当业务围绕聚合根展开,JPA 的 Unit of Work、脏检查和关联映射能减少大量样板代码。工单状态变更只需要加载聚合、执行领域方法、在事务结束时提交变化。这种写法让业务语言靠近代码。 但 JPA 不会自动替你做性能决策。惰性加载在详情页可能合理,在列表页却容易演变为 N+1:先查 N 条工单,再为每条工单查一次设备或负责人。解决方式不是把所有关联都设为 EAGER,而是为具体查询使用 fetch join、实体图或投影。
MyBatis 擅长保留 SQL 的形状
报表查询往往关心列、分组、排序和执行计划,而不是对象导航。MyBatis 允许把 SQL 保持为一等公民:CTE、窗口函数、索引提示和数据库方言都能清晰表达。对读模型来说,这种透明度比通用映射更重要。
SELECT device_id, COUNT(*) AS alert_count, MAX(created_at) AS last_alert_atFROM alert_eventWHERE created_at >= :startAtGROUP BY device_idORDER BY alert_count DESCLIMIT :limit这类结果应映射为查询 DTO,而不是硬塞进一个带生命周期的 Entity。
按查询形态做决定
| 场景 | 推荐重点 | 原因 |
| 聚合内状态变更 | JPA / ORM | 领域行为与事务一致性 |
| 简单详情与关联 | JPA 投影或 fetch join | 降低重复映射 |
| 复杂报表与批处理 | MyBatis | SQL 可控、易看执行计划 |
| 极高频热点读取 | 专用读模型 | 减少 ORM 开销与对象装配 |
讨论与反应