05 MyBatis 架构设计、渐进式综合项目与专家题库

本文件把 MyBatis 知识整合为可落地的系统能力:数据访问层架构、主线项目演进、模块边界、事务边界、多数据源、代码规范、专家清单和完整面试题答案。

1. 五层能力模型

层级 能力目标
入门 能配置 MyBatis,写 Mapper XML,完成 CRUD
进阶 能写动态 SQL、ResultMap、TypeHandler、分页、测试
高级 能理解执行流程、缓存、插件、事务和性能优化
精通 能治理 SQL、批处理、多环境、迁移、可观测性
专家 能设计数据访问架构、多数据源、读写分离、分库分表边界

2. 数据访问层架构

推荐分层:

text 复制代码
controller
  -> application service / service
    -> domain service
      -> repository / mapper
        -> database

MyBatis Mapper 不应承担业务规则,它只负责 SQL 和表映射。

3. Mapper 与 Repository

简单项目:

text 复制代码
Service -> Mapper

复杂项目:

text 复制代码
Service -> Repository -> Mapper

Repository 封装多个 Mapper、组装领域对象、隐藏数据库表结构。

java 复制代码
@Repository
public class CourseRepository {
  private final CourseMapper courseMapper;
  private final LessonMapper lessonMapper;

  public CourseDetail findDetail(Long courseId) {
    CourseDO course = courseMapper.selectById(courseId);
    List<LessonDO> lessons = lessonMapper.selectByCourseId(courseId);
    return CourseDetailAssembler.toDomain(course, lessons);
  }
}

4. DO、DTO、Domain 分离

text 复制代码
UserCreateRequest -> CreateUserCommand -> User domain -> UserDO -> users table

不要让数据库对象直接穿透到接口响应:

java 复制代码
// 不推荐
public UserDO getUser(Long id) {
  return userMapper.selectById(id);
}

更好:

java 复制代码
public UserResponse getUser(Long id) {
  User user = userRepository.findById(id);
  return UserResponse.from(user);
}

5. 主线项目:Knowledge Hub

业务背景:

  • 用户注册和登录。
  • 课程管理。
  • 章节管理。
  • 学习进度。
  • 订单和支付记录。
  • 搜索和筛选。
  • 后台管理。
  • 报表统计。

技术目标:

  • 用 MyBatis 管理数据访问。
  • 动态 SQL 支持复杂查询。
  • ResultMap 支持聚合对象。
  • Service 管理事务。
  • 测试覆盖 Mapper。
  • 性能优化支持大数据量查询。
  • 架构支持未来多数据源和读写分离。

6. Stage 1:用户 CRUD

表:

sql 复制代码
create table users (
  id bigint generated by default as identity primary key,
  username varchar(64) not null unique,
  email varchar(128) not null,
  status varchar(32) not null,
  created_at timestamp not null
);

Mapper:

java 复制代码
public interface UserMapper {
  UserDO selectById(Long id);
  int insert(UserDO user);
  int updateStatus(@Param("id") Long id, @Param("status") String status);
}

训练点:

  • Mapper XML。
  • 参数绑定。
  • 自增主键。
  • 基础事务。

7. Stage 2:课程搜索

需求:

  • keyword。
  • level。
  • status。
  • createdAt 范围。
  • 游标分页。

Query:

java 复制代码
public record CourseSearchQuery(
  String keyword,
  String level,
  String status,
  Long lastId,
  Integer limit
) {}

训练点:

  • where
  • if
  • 动态分页。
  • 索引设计。

8. Stage 3:课程详情聚合

课程详情包含:

  • course。
  • lessons。
  • teacher。
  • statistics。

策略:

  • 单个详情页可使用 join + ResultMap。
  • 大对象图可分多次查询组装。
  • 列表页避免一对多 join 破坏分页。

9. Stage 4:学习进度事务

需求:

  • 标记章节完成。
  • 更新课程进度。
  • 写学习日志。

Service:

java 复制代码
@Transactional
public void completeLesson(Long userId, Long lessonId) {
  progressMapper.upsertLessonProgress(userId, lessonId);
  progressMapper.recalculateCourseProgress(userId, lessonId);
  learningLogMapper.insert(userId, lessonId, "COMPLETE");
}

训练点:

  • 事务边界。
  • 幂等更新。
  • 唯一索引。
  • 并发安全。

10. Stage 5:订单与库存

需求:

  • 创建订单。
  • 扣减库存。
  • 防超卖。
  • 写订单日志。

SQL:

sql 复制代码
update course_stock
set stock = stock - #{quantity}
where course_id = #{courseId}
and stock >= #{quantity}

如果影响行数为 0,说明库存不足。

训练点:

  • 乐观并发控制。
  • 事务。
  • 行锁。
  • 异常回滚。

11. Stage 6:报表统计

需求:

  • 每日新增用户。
  • 课程完成率。
  • 热门课程。

策略:

  • 小规模直接 SQL 聚合。
  • 中等规模加索引和预聚合表。
  • 大规模用数仓、OLAP 或搜索引擎。

MyBatis 不应承担所有分析型查询压力。

12. Stage 7:多数据源与读写分离

场景:

  • 主库写。
  • 从库读。
  • 报表库。
  • 租户库。

风险:

  • 主从延迟。
  • 事务一致性。
  • 数据源路由错误。
  • 跨库事务复杂。

专家实践:

  • 事务内强制主库。
  • 写后读走主库或等待同步。
  • 路由逻辑集中。
  • 业务层不直接拼数据源。

13. Stage 8:分库分表边界

MyBatis 本身不是分库分表框架。可结合:

  • ShardingSphere。
  • 数据库中间件。
  • 应用层路由。

分库分表前先确认:

  • 单表数据量是否真的成为瓶颈。
  • 索引和归档是否已优化。
  • 查询维度是否稳定。
  • 跨分片查询如何处理。
  • 分布式事务是否可避免。

14. SQL 规范

建议:

  • Mapper 方法名表达业务查询意图。
  • 禁止 ${} 拼接用户输入。
  • 动态 SQL 不写复杂业务规则。
  • 查询必须明确列名,避免 select *
  • 大列表必须分页。
  • 更新必须带明确 where。
  • 重要 SQL 必须有测试。
  • 慢 SQL 必须 explain。

15. 目录规范

text 复制代码
src/main/java/com/example/knowledge/
├── user/
│   ├── controller/
│   ├── service/
│   ├── domain/
│   ├── repository/
│   └── mapper/
├── course/
├── order/
└── shared/

src/main/resources/mapper/
├── user/
├── course/
└── order/

按业务模块组织,避免所有 Mapper 堆在一个目录。

16. 专家架构检查清单

数据访问:

  • Mapper 是否只负责 SQL?
  • Service 是否控制事务?
  • 是否避免 Controller 直接调 Mapper?
  • 是否有 Repository 隔离复杂组装?

SQL:

  • 是否有注入风险?
  • 是否有 N+1?
  • 是否分页?
  • 是否有索引?
  • 是否 explain?

事务:

  • 边界是否正确?
  • 是否有自调用失效?
  • 是否捕获异常导致不回滚?
  • 是否存在长事务?

性能:

  • 是否深分页?
  • 是否返回过多列?
  • 是否批处理过大?
  • 是否连接池配置合理?

工程:

  • Mapper 测试是否覆盖关键 SQL?
  • 数据库迁移是否版本化?
  • SQL 日志是否脱敏?
  • 慢 SQL 是否可观测?

17. MyBatis 反模式

  • Controller 直接调用 Mapper。
  • XML 中写复杂业务规则。
  • ${} 拼接用户输入。
  • 所有查询都 select *
  • 列表页使用嵌套 select。
  • 不写 Mapper 测试。
  • 生产库手工改表。
  • 大事务包住远程调用。
  • 分页不考虑深分页。
  • 二级缓存无失效策略。
  • Mapper 返回 Map 贯穿业务层。
  • 数据库对象直接暴露给 API。

18. 面试题完整答案

18.1 MyBatis 适合什么架构位置?

MyBatis 适合放在数据访问层,负责 SQL 执行和数据库表映射。它不应承担业务规则、事务编排或接口响应组装。复杂项目中可在 Mapper 外增加 Repository,隔离表结构和领域模型。

18.2 为什么 Controller 不应该直接调用 Mapper?

Controller 负责 HTTP 协议和入参出参,不应知道数据库访问细节。直接调用 Mapper 会让事务、业务规则、权限和数据组装散落在接口层,难以复用和测试。应由 Service 或 Application Service 组织业务流程。

18.3 Repository 和 Mapper 有什么区别?

Mapper 是 MyBatis 的 SQL 映射接口,通常对应表或 SQL。Repository 是领域层或应用层的数据访问抽象,可组合多个 Mapper,隐藏数据库表结构,返回领域对象。简单项目可省略 Repository,复杂项目建议引入。

18.4 MyBatis 项目如何避免 SQL 混乱?

建立 SQL 规范:按业务模块组织 Mapper,方法命名表达意图,动态 SQL 控制复杂度,重要 SQL 有测试,禁止不安全 ${},统一分页和排序白名单,慢 SQL 进入审查流程。

18.5 如何设计事务边界?

事务边界应围绕一个业务用例,例如创建订单、完成课程、退款。事务内只放必要数据库操作,避免远程调用和长时间计算。事务应放 Service 层,并明确回滚异常。

18.6 多数据源如何治理?

数据源路由应集中处理,业务代码不应到处手动选择数据源。事务内必须明确数据源,读写分离要处理主从延迟,写后读要走主库或有一致性策略。跨库事务应尽量通过业务设计避免。

18.7 分库分表前要考虑什么?

先确认瓶颈是否真的来自单库单表。要评估索引、归档、缓存、读写分离是否已足够。分片键要稳定,跨分片查询、分页、事务、唯一 ID、数据迁移和运维成本都必须提前设计。

18.8 如何判断一个 MyBatis 开发者达到专家水平?

不仅能写 CRUD,还能理解执行链路、动态 SQL、ResultMap、缓存、事务、插件、性能、安全和架构边界。专家能设计数据访问规范,定位慢 SQL,治理事务和多数据源,并为未来扩展留下迁移路径。

19. 终极知识点总表

基础:

  • SqlSessionFactory。
  • SqlSession。
  • Mapper。
  • Mapper XML。
  • #{} / ${}
  • CRUD。

进阶:

  • Dynamic SQL。
  • ResultMap。
  • association。
  • collection。
  • TypeHandler。
  • Spring Boot Starter。
  • Transaction。

高级:

  • Executor。
  • MappedStatement。
  • BoundSql。
  • ParameterHandler。
  • ResultSetHandler。
  • Cache。
  • Plugin。

精通:

  • Batch。
  • Pagination。
  • Testcontainers。
  • Flyway。
  • SQL explain。
  • Connection Pool。
  • Observability。

专家:

  • Repository。
  • 多数据源。
  • 读写分离。
  • 分库分表。
  • 缓存一致性。
  • 数据访问治理。
  • SQL 安全审计。

20. 项目演进路线扩展

20.1 v1 单体 CRUD

能力:

  • 用户、课程、章节基础表。
  • Mapper XML CRUD。
  • Service 事务。
  • H2 本地测试。

重点不是功能多,而是建立规范。

20.2 v2 复杂查询

能力:

  • 课程搜索。
  • 多条件筛选。
  • 游标分页。
  • 课程卡片 DTO。
  • Mapper 测试覆盖动态 SQL。

20.3 v3 聚合详情

能力:

  • 课程详情。
  • 章节列表。
  • 讲师信息。
  • 学习统计。

架构选择:

  • 简单详情可 ResultMap join。
  • 复杂详情用 Repository 多次查询组装。

20.4 v4 事务用例

能力:

  • 报名课程。
  • 扣减库存。
  • 写学习进度。
  • 写审计日志。

重点:

  • 幂等。
  • 乐观锁。
  • 唯一约束。
  • 事务回滚。

20.5 v5 性能治理

能力:

  • 慢 SQL 日志。
  • explain 审查。
  • 索引优化。
  • N+1 消除。
  • 连接池监控。

20.6 v6 架构升级

能力:

  • Repository。
  • 多数据源。
  • 读写分离。
  • 缓存层。
  • 数据迁移。
  • ADR。

21. 数据访问层 ADR 示例

md 复制代码
# ADR: 课程详情使用 Repository 组装而不是深层 ResultMap

## 背景

课程详情包含课程、章节、讲师、统计数据。单条 join SQL 行数膨胀,ResultMap 复杂。

## 决策

使用 CourseRepository 分别调用 CourseMapper、LessonMapper、TeacherMapper、StatisticMapper,然后组装 CourseDetail。

## 后果

优点:SQL 简单、可测试、易优化。
代价:需要额外组装代码,并注意批量查询避免 N+1。

22. 团队 SQL Review 清单

  • 是否使用 #{} 绑定业务值?
  • 是否存在 ${}?是否白名单?
  • 是否明确列名?
  • where 条件是否完整?
  • update/delete 是否可能全表?
  • 是否有合适索引?
  • 是否有深分页?
  • 是否有 N+1?
  • 是否返回过多数据?
  • 是否有 Mapper 测试?
  • 是否需要 explain?

23. 专家场景题

23.1 课程搜索越来越慢怎么办?

先看 SQL 日志和 explain,确认是否索引失效、like 前缀通配、排序 filesort、返回数据过多。短期可加索引、改游标分页、减少列。中期可预计算热字段。若搜索复杂度高,考虑 Elasticsearch/OpenSearch,而不是继续用 MyBatis 承担全文搜索。

23.2 学习进度并发更新如何保证正确?

使用唯一约束保证幂等,如 (user_id, lesson_id) 唯一。完成操作可 upsert。课程进度更新可基于数据库聚合重新计算,或使用乐观锁版本号。事务内保持更新顺序一致,避免死锁。

23.3 多租户如何设计?

简单场景可每张表加 tenant_id 并在所有查询中显式过滤。复杂场景可使用 schema/db 隔离。插件可做兜底,但不能替代 SQL 规范和测试。必须防止跨租户访问,服务端权限和数据条件都要校验。

24. 架构专家题补充答案

24.1 MyBatis 项目什么时候需要 Repository?

当单个业务查询需要组合多个 Mapper、隐藏表结构、返回领域对象、隔离 DO 和 Domain 时,需要 Repository。简单 CRUD 可直接 Service 调 Mapper,但复杂业务中 Repository 能降低 Service 对数据库结构的耦合。

24.2 MyBatis 能否用于领域驱动设计?

可以,但不要让 Mapper 直接成为领域模型的一部分。Mapper 属于基础设施层,Repository 负责把数据库对象转换为领域对象。领域层不应依赖 MyBatis API 或 XML。

24.3 如何让 MyBatis 系统可持续演进?

建立 SQL 规范、Mapper 测试、数据库迁移、性能监控、慢 SQL 审查、分层架构和 ADR。随着复杂度上升,引入 Repository、缓存层、多数据源治理和读写分离,但不要过早复杂化。

25. 终极专家检查清单

基础:

  • Mapper namespace 正确。
  • 参数命名清晰。
  • XML 被扫描。
  • #{} 使用正确。

动态 SQL:

  • 空条件安全。
  • 空集合安全。
  • 排序白名单。
  • OGNL 简单。

映射:

  • ResultMap 有 id。
  • 列别名清晰。
  • 一对多分页安全。
  • TypeHandler 无副作用。

事务:

  • Service 控制边界。
  • 无自调用失效。
  • 异常回滚明确。
  • 无长事务。

性能:

  • SQL explain。
  • 索引匹配。
  • 无 N+1。
  • 无深分页。
  • 连接池合理。

安全:

  • 无 SQL 注入。
  • 日志脱敏。
  • 权限在服务端。
  • 多租户隔离。

架构:

  • DO/DTO/Domain 分离。
  • Mapper 不承载业务。
  • Repository 边界清晰。
  • 多数据源集中治理。
  • 数据迁移版本化。
相关推荐
空中海4 小时前
03 MyBatis Spring Boot 集成、事务、测试与工程化体系
spring boot·后端·mybatis
Nicander2 天前
理解 mybatis 源码:vibe-coding一个mini-mybatis
后端·mybatis
庞轩px2 天前
致远互联实习复盘:一条SQL替代300次循环查询,组织架构选择器从5秒降到300毫秒
java·sql·mysql·mybatis·实习经历·n+1问题·join联表查询
952363 天前
MyBatis
后端·spring·mybatis
misL NITL4 天前
idea、mybatis报错Property ‘sqlSessionFactory‘ or ‘sqlSessionTemplate‘ are required
tomcat·intellij-idea·mybatis
是宇写的啊4 天前
MyBatis-Plus
java·开发语言·mybatis
工作log5 天前
Spring Boot 3.5 + MyBatis Plus + RabbitMQ:打造 AI 驱动的慢 SQL 监控与优化系统
spring boot·mybatis·java-rabbitmq
河阿里5 天前
MyBatis-Plus:MyBatis的进阶开发
数据库·mybatis
橙子圆1235 天前
Mybatis之动态sql
sql·tomcat·mybatis