在 Java 持久层框架中,MyBatis 和 MyBatis-Plus 是开发者最常讨论的两个工具。它们看似相似,实则定位和功能差异显著。本文将通过核心特性对比、场景化案例和性能分析,帮助你彻底理解二者的区别,并给出实际项目中的选型建议。
一、核心定位差异
1. MyBatis:灵活的 SQL 映射框架
- 本质:基于 XML/注解的 SQL 映射工具,提供高度自由的 SQL 控制能力。
- 核心价值:
- 将 SQL 与 Java 代码解耦,支持复杂查询优化。
- 适合对 SQL 有精细化控制需求的场景(如金融系统、大数据分析)。
2. MyBatis-Plus(MP):MyBatis 的增强工具包
- 本质:在 MyBatis 基础上封装通用 CRUD 操作和开发工具链。
- 核心价值:
- 减少 80% 的重复代码(如单表 CRUD)。
- 提供开箱即用的功能(如分页、逻辑删除、代码生成器)。
二、功能对比与场景化示例
1. 基础 CRUD 实现
场景:实现用户表的增删改查
-
MyBatis 方案
需手动编写 Mapper 接口、XML 文件和 SQL:
<!-- UserMapper.xml -->
<insert id="insert" parameterType="User">
INSERT INTO user (name, age) VALUES (#{name}, #{age})
</insert>
<select id="selectById" resultType="User">
SELECT * FROM user WHERE id = #{id}
</select>
需为每个实体类重复编写类似代码。
-
MyBatis-Plus 方案
继承BaseMapper<T>
接口即可自动获得通用方法:
public interface UserMapper extends BaseMapper<User> {}
// 直接调用 userMapper.insert(user) 或 userMapper.selectById(1);
无需编写 XML,开发效率提升显著。
2. 复杂查询构建
场景:查询年龄大于 20 岁且姓名包含 "John" 的用户
-
MyBatis 方案
需手动拼接动态 SQL(或在 XML 中使用<if>
标签):
// Java 代码中拼接 SQL
String nameParam = "%John%";
List<User> users = sqlSession.selectList(
"com.example.mapper.UserMapper.selectByCondition",
Map.of("age", 20, "name", nameParam)
);
<!-- XML 中动态 SQL -->
<select id="selectByCondition" resultType="User">
SELECT * FROM user
WHERE age > #{age}
<if test="name != null">
AND name LIKE #{name}
</if>
</select>
MyBatis-Plus 方案
使用 QueryWrapper
或 LambdaQueryWrapper
链式构建条件:
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.gt("age", 20)
.like("name", "John");
List<User> users = userMapper.selectList(wrapper);
// Lambda 表达式(避免字段名硬编码)
LambdaQueryWrapper<User> lambdaWrapper = new LambdaQueryWrapper<>();
lambdaWrapper.gt(User::getAge, 20)
.like(User::getName, "John");
代码更简洁,且避免 SQL 注入风险。
3. 分页功能实现
场景:实现用户列表分页查询(每页 10 条)
MyBatis 方案
需依赖第三方插件(如 PageHelper)或手动计算分页参数:
// PageHelper 插件
PageHelper.startPage(1, 10);
List<User> users = userMapper.selectAll();
PageInfo<User> pageInfo = new PageInfo<>(users);
MyBatis-Plus 方案
内置分页插件,直接使用 Page
对象:
// 配置分页插件(Spring Boot)
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor());
return interceptor;
}
// 业务代码
Page<User> page = new Page<>(1, 10);
IPage<User> userPage = userMapper.selectPage(page, null);
List<User> users = userPage.getRecords(); // 获取当前页数据
4. 代码生成器对比
场景:快速生成 Entity、Mapper、Service 层代码
MyBatis 方案
依赖 MyBatis Generator 插件,需配置 generatorConfig.xml
:
<table tableName="user" domainObjectName="User">
<generatedKey column="id" sqlStatement="MySQL" identity="true"/>
</table>
MyBatis-Plus 方案
内置代码生成器,支持一键生成全套代码:
AutoGenerator generator = new AutoGenerator();
// 配置数据源
generator.setDataSource(new DataSourceConfig()
.setUrl("jdbc:mysql://localhost:3306/db")
.setUsername("root")
.setPassword("root"));
// 配置全局策略
generator.setGlobalConfig(new GlobalConfig()
.setOutputDir(System.getProperty("user.dir") + "/src/main/java")
.setAuthor("Dev"));
// 执行生成
generator.execute();
三、选型决策树
根据项目需求选择:
-
是否需要高度定制化 SQL?
- 是 → 选择 MyBatis(如复杂联表查询、存储过程调用)。
- 否 → 进入下一步。
-
是否希望减少重复 CRUD 代码?
- 是 → 选择 MyBatis-Plus。
- 否 → 选择 MyBatis。
-
是否需要快速生成基础代码?
- 是 → 选择 MyBatis-Plus(内置生成器)。
- 否 → 选择 MyBatis。
四、性能与兼容性
1. 性能对比
- 简单查询:MyBatis-Plus 通过条件构造器生成的 SQL 性能与手写 SQL 基本一致。
- 复杂查询:建议手动编写 XML/注解 SQL(避免自动生成的 SQL 不够优化)。
2. 兼容性
- MyBatis-Plus 完全兼容 MyBatis 的所有功能,原有 XML 和注解 SQL 可继续使用。
- 迁移成本低,可在存量项目中逐步替换 CRUD 操作为 MyBatis-Plus。
五、总结:什么场景用哪个?
框架 | 适用场景 | 不适用场景 |
---|---|---|
MyBatis | 1. 复杂 SQL 优化需求 2. 存量项目维护 | 需要快速开发 CRUD 功能的项目 |
MyBatis-Plus | 1. 新项目快速迭代 2. 单表操作占比高 | 需要深度定制 SQL 的复杂系统 |
推荐策略:
- 中小型项目:直接使用 MyBatis-Plus,大幅提升开发效率。
- 大型复杂系统:以 MyBatis 为核心,结合 MyBatis-Plus 的通用功能(如分页、代码生成)。
- 混合使用:在同一个项目中,复杂 SQL 用手写 XML,简单 CRUD 用 MyBatis-Plus。