MyBatis-Plus
Mybatis-Plus
在学习微服务之前,我们再次重温MybatisPlus的内容
个人理解,MybatisPlus提供了非常简便的接口以供快速开发单表的增删改查,所以非常契合微服务架构
条件构造器
尤其是在使用体验和对SQL语句的封装程度上,Mybatis-Plus的Wrapper机制确实与EF Core的LINQ查询有异曲同工之妙,使得两者的调用方式在某些场景下显得非常相似
Mybatis-Plus的Wrapper机制,特别是其对Lambda表达式的支持,确实在很大程度上提供了与EF Core的LINQ查询相似的、面向对象的、流式化的SQL构建体验,极大地提升了开发效率
QueryWrapper&UpdateWrapper
当单纯的使用where作为条件拼接时,QueryWrapper可以完美的替代UpdateWrapper
在默认设置的更新策略下Redis(NOT_NULL),是不可以将数据库字段更新为空值的,但使用UpdateWrapper的set方法可以针对字段更新,那么也就可以将指定字段更新为null,这是 UpdateWrapper 的一个重要优势
UpdateWrapper提供了setSql()方法,允许你在SET子句中直接写入SQL片段,这对于实现column = column + 1(自增)、update_time = NOW()(更新时间戳)等操作非常有用
整合
使用wrapper作为where的条件是正确思路,但直接将一些select语句写在service层又不符合企业规范
所以可以将条件的构建交给mybatisplus接管,使用wrapper构建,其他的sql语句仍然写在mapper.xml中
需要注意的是,在mapper层给mapper.xml传wrapper时,我们需要将wrapper参数加上 Param注解,且注解的值必须为ew
或者调用Constants.WRAPPER获取常量
哑巴“mapper”
理想的“哑巴”Mapper:
如果 Mapper 层是绝对的“哑巴”,它应该只知道如何将 User 实体对象映射到 user 表,并提供最基本的 CRUD 操作(insert(User)、update(User)、selectById(id)、deleteById(id))。
它的方法签名和 XML 定义应该严格对应数据库的单表操作,不涉及任何业务概念上的“视图”或“聚合”。
在这种理想情况下,任何涉及多表联查、复杂聚合、或者需要特定字段组合的“业务视图”,都应该由 Service 层通过调用多个 Mapper 的单表方法,然后在内存中进行数据组装和处理。
Mybatis 的现实与优势:
Mybatis 的核心优势就是它能够让你直接编写 SQL。这意味着你可以利用数据库的强大能力来执行复杂的 JOIN、子查询、聚合函数、窗口函数等。
如果 Service 层需要一个“用户及其所有订单”的视图,让数据库通过一个 JOIN 查询一次性返回,通常比 Service 层先查用户,再循环查每个用户的订单(N+1 问题),或者查所有用户和所有订单然后在内存中匹配,要高效得多。
Mybatis 的 resultMap 机制也使得将复杂的 JOIN 结果直接映射到复杂的 DTO/VO 对象变得非常方便,省去了 Service 层手动组装的繁琐。
在 Mybatis 的世界里,我们确实是利用了它方便编写 SQL 的特性,在 Mapper 层(特别是 XML 文件中)定义了许多针对 Service 层特定业务需求而定制的“抽象视图”或“聚合查询”。
这是一种务实且高效的架构选择,它在一定程度上“牺牲”了 Mapper 层的绝对“哑巴”特性,以换取更优的性能、更简洁的 Service 层代码(无需手动组装复杂对象),以及更灵活的 SQL 控制能力。这种折中在 Mybatis 社区中是普遍接受的最佳实践。
MybatisPlus的最佳实践
在微服务架构下,Mybatis-Plus 和 Mybatis 并非互斥,而是相辅相成的强大工具。理解它们的优势与适用场景,并遵循清晰的服务边界原则,是构建高效、可维护系统的关键。
1. Mybatis (XML) 最佳实践:复杂查询与性能优化
在服务内部,当面临需要高性能、复杂数据聚合的查询场景时,我们可以在 Mybatis 的 XML Mapper 中编写定制化的 SQL。
- 妥协“哑巴原则”: 在这种特定场景下,为了追求极致的性能和灵活性,我们可以适度偏离严格的“哑巴原则”(即 SQL 仅负责简单的 CRUD 映射)。
- 采用“抽象视图原则”: 即在 XML 中编写复杂的 SQL(如多表 JOIN、子查询、聚合函数、窗口函数等),以构建针对特定业务需求的“数据视图”。
- 优势: 这种方式能够充分利用数据库的优化能力,实现高性能的复杂查询,减少应用层的内存组装和网络往返。对于服务内部的复杂报表、统计或特定业务视图而言,其性能优势是显著的。
- 局限性: 虽然可能降低 SQL 的通用复用性,因为这些 SQL 通常是为特定业务场景量身定制的。
- 重要前提: 此实践仅限于服务内部的数据操作。
2. Mybatis-Plus 最佳实践:单表操作与动态条件
Mybatis-Plus 在微服务架构中展现出巨大优势,尤其适用于处理服务内部的单表 CRUD (增删改查) 操作。
- 简化单表操作: Mybatis-Plus 提供了大量开箱即用的通用方法,极大地简化了单表操作的开发工作,减少了大量的样板代码。这与微服务中每个服务通常聚焦于其核心数据模型的特点高度契合。
- 强大的 Wrapper 机制: 其核心亮点在于强大的
Wrapper机制。通过Wrapper,我们可以在 Java 代码中灵活、类型安全地拼接各种动态的WHERE条件、排序、分组等,以满足不同业务场景下对单表数据的查询需求。 - 优势: 这使得业务逻辑层能够根据运行时参数动态构建查询,而无需为每种条件组合编写独立的 SQL,提高了开发效率和代码可维护性。
3. 核心准则:服务边界与数据所有权
Mybatis-Plus 和 Mybatis 的协同使用,应严格遵循微服务架构的核心原则——服务边界与数据所有权。
- 黄金准则: 同一服务内的多张表应该 JOIN,跨服务的表不应该 JOIN!
- 服务内 JOIN 的合理性:
- 服务内部的表通常属于同一个业务领域,共同构成该服务的数据模型。
- 在同一个数据库实例内进行 JOIN 操作,可以保证事务一致性,并利用数据库的优化能力,实现高效的数据聚合。
- 这符合服务内部数据内聚的原则。
- 跨服务 JOIN 的危害:
- 打破封装性: 直接跨服务 JOIN 数据库表会严重破坏服务的封装性,侵入其他服务的内部实现细节。
- 紧密耦合: 导致服务间紧密耦合,一旦被 JOIN 的服务修改了数据库结构,依赖方将立即受影响,阻碍服务的独立部署、独立演进和弹性伸缩。
- 分布式事务复杂性: 跨库 JOIN 无法在一个本地事务中完成,若涉及数据一致性,将引入复杂的分布式事务问题。