Java 项目里,持久层最容易变成“什么都能查”的地方。Mapper、SQL、缓存、事务、分页、批量写入、外部存储访问都混在一起,短期开发很快,长期维护会很痛。
不管底层是 MySQL、HBase 还是其他存储,持久层都应该先把边界拆清楚。
MyBatis 适合关系型 SQL
MyBatis 的优势是 SQL 可控,适合复杂查询、手写 join、分页和精细化索引优化。
常见结构是:
Controller -> Service -> Repository/Mapper -> Database
Mapper 只负责数据访问,不应该塞业务判断。复杂业务逻辑放在 Service,SQL 只表达数据读取和写入。
如果 Mapper 方法越来越像业务接口,比如 finishOrderAndNotifyUser,就说明边界已经混了。
SQL 要有明确输入输出
MyBatis 方法要尽量让入参和返回值清楚。
建议:
- 查询条件用专门 Query 对象。
- 更新入参用 Command 或 Entity。
- 返回值不要随意用
Map<String, Object>。 - 分页参数和排序字段要校验。
动态 SQL 很方便,但不能让任意字段拼进 SQL。尤其是排序字段、表名、列名这类不能用普通参数占位的内容,必须白名单控制。
HBase 适合特定访问模型
HBase 不是关系型数据库替代品。它更适合大规模、按 rowkey 或范围扫描组织的数据访问。
使用 HBase 前要先想清楚:
- rowkey 怎么设计。
- 查询是否能按 rowkey 命中。
- 是否需要二级索引。
- 数据是否适合宽表模型。
- 扫描范围是否可控。
如果业务大量依赖多条件随机查询,直接套 HBase 往往会很难受。
持久层不要泄漏存储细节
Service 层不应该到处感知底层是 MySQL 还是 HBase。比较好的做法是通过 Repository 屏蔽存储细节:
public interface UserRepository {
User findById(Long id);
void save(User user);
}
底层可以有 MyBatis 实现,也可以有 HBase 实现。业务层关心的是领域对象和操作语义,而不是 SQL 细节或 rowkey 拼接规则。
实用结论
Java 持久层的核心是边界:MyBatis 管关系型 SQL,HBase 管特定大数据访问模型,Service 管业务流程。
Mapper 不写业务,动态 SQL 有白名单,HBase 先设计 rowkey,Repository 屏蔽存储细节。这样持久层才不会从“数据访问”慢慢变成系统里最难改的一层。




