【MyBatis 与 MyBatis Plus 对比(代码实战讲解)】
MyBatis 与 MyBatis Plus 对比(代码实战讲解)
MyBatis 是经典的持久层框架,核心是手动编写 SQL 映射;而 MyBatis Plus(简称 MP)是 MyBatis 的增强工具,通过自动化 CRUD、条件构造器、代码生成器等特性简化开发。以下通过具体代码示例,从 CRUD 操作、动态 SQL、扩展功能三个维度对比两者差异。
一、环境准备
假设我们有一个 User 实体类,对应数据库表 t_user(字段:id、name、age、email)。
实体类定义
@Data
public class User {
private Long id;
private String name;
private Integer age;
private String email;
}
二、CRUD 操作对比
1. 查询单个用户(根据 ID)
MyBatis 实现
需要编写 Mapper 接口 + XML 映射文件:
- Mapper 接口(UserMapper.java):
public interface UserMapper {
User selectById(Long id); // 方法名与 XML 中的 SQL id 对应
}
- XML 映射文件(UserMapper.xml):
<mapper namespace="com.example.mapper.UserMapper">
<select id="selectById" resultType="com.example.entity.User">
SELECT id, name, age, email FROM t_user WHERE id = #{id}
</select>
</mapper>
使用:
User user = userMapper.selectById(1L); // 需手动编写 XML 中的 SQL
MyBatis Plus 实现
无需编写 XML,直接继承 BaseMapper 接口,内置通用 CRUD 方法:
- Mapper 接口(UserMapper.java):
public interface UserMapper extends BaseMapper<User> {
// 无需额外方法,直接继承 BaseMapper 的 selectById
}
使用:
User user = userMapper.selectById(1L); // 直接调用内置方法,无需写 SQL
差异总结:MyBatis 需手动编写 XML 和 SQL;MP 内置通用方法,直接调用即可。
2. 插入用户
MyBatis 实现
- XML 映射文件(UserMapper.xml):
<insert id="insert" parameterType="com.example.entity.User">
INSERT INTO t_user (name, age, email)
VALUES (#{name}, #{age}, #{email})
</insert>
Mapper 接口:
public interface UserMapper {
int insert(User user); // 方法名与 XML 中的 insert id 对应
}
使用:
User user = new User();
user.setName("张三");
user.setAge(20);
user.setEmail("zhangsan@example.com");
userMapper.insert(user); // 需手动写 XML 的 INSERT 语句
MyBatis Plus 实现
直接调用 BaseMapper 内置的 insert 方法:
User user = new User();
user.setName("张三");
user.setAge(20);
user.setEmail("zhangsan@example.com");
userMapper.insert(user); // 无需写 SQL,MP 自动生成 INSERT 语句
差异总结:MyBatis 需手动编写 INSERT 语句;MP 自动根据实体类字段生成 SQL。
3. 条件查询(多条件组合)
假设需要查询“年龄大于 18 岁且邮箱包含 ‘example’ 的用户”。
MyBatis 实现
通过 XML 的动态 SQL 标签(、)拼接条件:
- XML 映射文件(UserMapper.xml):
<select id="selectByCondition" resultType="com.example.entity.User">
SELECT * FROM t_user
<where>
<if test="age != null">
AND age > #{age}
</if>
<if test="email != null and email != ''">
AND email LIKE CONCAT('%', #{email}, '%')
</if>
</where>
</select>
Mapper 接口:
public interface UserMapper {
List<User> selectByCondition(@Param("age") Integer age, @Param("email") String email);
}
使用:
List<User> users = userMapper.selectByCondition(18, "example");
// 需手动维护 XML 中的条件逻辑,字段名硬编码易出错
MyBatis Plus 实现
通过 QueryWrapper 或 LambdaQueryWrapper 链式构建条件(推荐 Lambda 避免字段名硬编码):
// 使用 LambdaQueryWrapper(推荐)
List<User> users = userMapper.selectList(
Wrappers.<User>lambdaQuery()
.gt(User::getAge, 18) // 年龄 > 18
.like(User::getEmail, "example") // 邮箱包含 "example"
);
// 等价于 MyBatis XML 中的动态 SQL 拼接,但无需手动写 XML
差异总结:
- MyBatis 需通过 XML 动态标签拼接 SQL,字段名需与参数名严格匹配(易出错)。
- MP 通过 Lambda 表达式链式调用,自动关联实体类字段,代码更简洁、安全。
三、动态 SQL 对比
MyBatis 的核心优势是灵活控制 SQL,适合复杂查询;MP 则通过封装简化了这一过程,但复杂场景仍可结合 MyBatis 原生能力。
1. 示例:多表联查(用户 + 订单)
假设需要查询“用户及其最近一笔订单”(用户表 t_user,订单表 t_order)。
MyBatis 实现
通过 XML 编写复杂 SQL:
<select id="selectUserWithOrder" resultMap="userOrderMap">
SELECT u.*, o.order_no, o.amount
FROM t_user u
LEFT JOIN t_order o ON u.id = o.user_id
WHERE u.id = #{userId}
ORDER BY o.create_time DESC
LIMIT 1
</select>
<resultMap id="userOrderMap" type="com.example.entity.User">
<id column="id" property="id"/>
<result column="name" property="name"/>
<!-- 嵌套订单信息 -->
<association property="order" column="id" select="selectOrderByUserId"/>
</resultMap>
<select id="selectOrderByUserId" resultType="com.example.entity.Order">
SELECT order_no, amount FROM t_order WHERE user_id = #{userId} ORDER BY create_time DESC LIMIT 1
</select>
特点:需手动编写多表联查 SQL,逻辑集中在 XML 中,适合复杂场景。
MyBatis Plus 实现
MP 更适合单表操作,多表联查仍需结合 MyBatis 原生能力(或使用 MP 的 @TableField(exist = false) 扩展):
// 方式1:直接写 XML(与 MyBatis 一致)
// 方式2:使用 MP 的 LambdaQueryWrapper 结合自定义 SQL
List<User> users = userMapper.selectList(
Wrappers.<User>lambdaQuery()
.eq(User::getId, 1L)
// 自定义 SQL 片段(需在 XML 中配置)
.apply("EXISTS (SELECT 1 FROM t_order o WHERE o.user_id = t_user.id AND o.amount > 100)")
);
特点:MP 未完全封装多表联查,复杂场景仍需依赖 MyBatis 原生 XML 或自定义 SQL。
四、扩展功能对比
1. 代码生成器
MP 内置代码生成器,可自动生成实体类、Mapper、Service 等代码,大幅减少重复劳动。
配置示例(generatorConfig.java):
public class Generator {
public static void main(String[] args) {
AutoGenerator ag = new AutoGenerator(new DataSourceConfig.Builder("jdbc:mysql://...", "user", "pass").build());
ag.global(GlobalConfig.builder().outputDir("D:/code").author("test").build())
.packageInfo(PackageConfig.builder().parent("com.example").entity("entity").mapper("mapper").build())
.strategy(StrategyConfig.builder().addInclude("t_user").build()); // 指定数据库表
ag.execute();
}
}
效果:运行后自动生成 User.java(实体类)、UserMapper.java(Mapper 接口)、UserMapper.xml(XML 文件)等。
2. 分页插件
MP 内置分页插件,简化物理分页实现(无需手动写 LIMIT 语句)。
配置与使用:
// 1. 配置分页插件(Spring Boot 中)
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return interceptor;
}
// 2. 使用分页查询
Page<User> page = new Page<>(1, 10); // 第1页,每页10条
userMapper.selectPage(page, Wrappers.emptyWrapper());
List<User> list = page.getRecords(); // 当前页数据
long total = page.getTotal(); // 总记录数
MyBatis 实现分页:需手动编写 LIMIT #{start}, #{size},或使用 PageHelper 插件(非 MP 原生)。
五、核心差异总结
| 维度 | MyBatis | MyBatis Plus |
|---|---|---|
| CRUD 操作 | 需手动编写 XML 和 SQL | 内置 BaseMapper,自动生成基础 CRUD |
| 动态 SQL | 通过 、 等标签灵活控制 | 通过 LambdaQueryWrapper 链式调用简化 |
| 多表联查 | 完全手动编写复杂 SQL | 依赖原生 XML 或自定义 SQL |
| 代码生成 | 需手动创建或使用第三方工具 | 内置代码生成器,自动生成实体类等 |
| 学习成本 | 需掌握 XML 配置、动态 SQL 等 | 学习成本低,适合快速上手 |
| 适用场景 | 复杂 SQL、高度定制化需求 | 单表操作为主、快速迭代项目 |
六、总结
- MyBatis 是“灵活工匠”,适合需要精细控制 SQL 的复杂场景(如多表联查、存储过程)。
- MyBatis Plus 是“高效助手”,通过自动化 CRUD、条件构造器等特性,大幅降低单表操作的开发成本,适合快速迭代的业务系统。
实际项目中:推荐以 MP 为基础,处理 80% 的基础 CRUD;对于 20% 的复杂查询(如多表关联、高性能优化),仍使用 MyBatis 原生 XML 或自定义 SQL,兼顾效率与灵活性。
更多推荐


所有评论(0)