🔍 MyBatis 与 MyBatis-Plus 的对比与选择
文章目录
- 🔍 MyBatis 与 MyBatis-Plus 的对比与选择
- 🧠 一、MyBatis 核心回顾
- 💡 核心思想与架构定位
- ⚡ 基础使用示例
- ⚠️ MyBatis 的痛点
- ⚡ 二、MyBatis-Plus 功能特性解析
- 💡 MyBatis-Plus 定位
- 🚀 1. 单表 CRUD 封装
- 🔧 2. 条件构造器(Wrapper)
- 🛠️ 3. 插件体系
- 🚀 三、代码生成与快速开发
- 💡 代码生成器配置
- ⚡ 生成结果结构
- ⚖️ 四、全方位对比分析
- 💡 核心特性对比
- 📊 性能对比
- 🔧 代码量对比
- 🎯 五、适用场景与选型指南
- 💡 技术选型决策框架
- 🎯 具体场景建议
- 📝 选型检查清单
- 🔧 六、混合使用策略
- 💡 共存与渐进式迁移
- ⚡ 混合使用示例
- 🔧 迁移策略建议
- 🔚 总结与建议
- 📚 核心结论
- 🎯 最终建议
🧠 一、MyBatis 核心回顾
💡 核心思想与架构定位
MyBatis 作为半自动化的 ORM 框架,其设计哲学是:
MyBatis 的核心价值:
- 🎯 SQL可控性:开发者完全掌握SQL优化
- 🔧 灵活性:支持复杂查询和存储过程
- 📊 映射简单:ResultMap实现对象关系映射
- 🛡️ 稳定性:经过大量生产环境验证
⚡ 基础使用示例
// 传统MyBatis的Mapper接口
public interface UserMapper {@Select("SELECT * FROM users WHERE id = #{id}")User selectById(Integer id);@Insert("INSERT INTO users(name, email) VALUES(#{name}, #{email})")@Options(useGeneratedKeys = true, keyProperty = "id")int insert(User user);@Update("UPDATE users SET name=#{name} WHERE id=#{id}")int update(User user);@Delete("DELETE FROM users WHERE id = #{id}")int delete(Integer id);
}
XML 映射文件:
<!-- UserMapper.xml -->
<mapper namespace="com.example.mapper.UserMapper"><select id="selectByCondition" resultType="User">SELECT * FROM usersWHERE status = #{status}<if test="name != null">AND name LIKE CONCAT('%', #{name}, '%')</if>ORDER BY id DESC</select>
</mapper>
⚠️ MyBatis 的痛点
痛点 | 描述 | 影响 |
---|---|---|
重复CRUD | 每个实体都需要基础CRUD方法 | 开发效率低 |
SQL编写 | 简单查询也需要手动编写SQL | 代码冗余 |
分页复杂 | 需要插件或手动实现分页 | 一致性差 |
代码生成 | 缺乏官方代码生成工具 | 初期搭建繁琐 |
⚡ 二、MyBatis-Plus 功能特性解析
💡 MyBatis-Plus 定位
MyBatis-Plus 是 MyBatis 的增强工具,在保留 MyBatis 所有特性的基础上,提供了更便捷的开发体验:
🚀 1. 单表 CRUD 封装
Mapper 接口继承:
// 只需继承BaseMapper即可获得完整CRUD能力
public interface UserMapper extends BaseMapper<User> {// 无需编写基础CRUD方法// 自定义复杂查询方法List<User> selectComplexQuery(UserQuery query);
}// 服务层使用
@Service
public class UserService {@Autowiredprivate UserMapper userMapper;public void addUser(User user) {// 直接使用MyBatis-Plus提供的方法int result = userMapper.insert(user);}public User getUserById(Long id) {return userMapper.selectById(id);}public List<User> getUsersByCondition() {// 使用QueryWrapper构建查询条件QueryWrapper<User> wrapper = new QueryWrapper<>();wrapper.eq("status", 1).like("name", "张").orderByDesc("create_time");return userMapper.selectList(wrapper);}
}
🔧 2. 条件构造器(Wrapper)
Lambda 表达式条件构造:
// Lambda条件构造器(推荐)
public List<User> getActiveUsers(String keyword) {LambdaQueryWrapper<User> wrapper = Wrappers.lambdaQuery();wrapper.eq(User::getStatus, 1).like(StringUtils.isNotBlank(keyword), User::getName, keyword).between(User::getCreateTime, startDate, endDate).orderByDesc(User::getCreateTime);return userMapper.selectList(wrapper);
}// 更新操作条件构造
public int updateUserStatus(Long userId, Integer status) {User user = new User();user.setStatus(status);UpdateWrapper<User> wrapper = new UpdateWrapper<>();wrapper.eq("id", userId).eq("status", 0); // 只更新状态为0的用户return userMapper.update(user, wrapper);
}
🛠️ 3. 插件体系
常用插件配置:
@Configuration
public class MybatisPlusConfig {@Beanpublic MybatisPlusInterceptor mybatisPlusInterceptor() {MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();// 分页插件PaginationInnerInterceptor paginationInterceptor = new PaginationInnerInterceptor();paginationInterceptor.setDbType(DbType.MYSQL);paginationInterceptor.setMaxLimit(1000L); // 单页最大条数interceptor.addInnerInterceptor(paginationInterceptor);// 乐观锁插件OptimisticLockerInnerInterceptor optimisticLockerInterceptor = new OptimisticLockerInnerInterceptor();interceptor.addInnerInterceptor(optimisticLockerInterceptor);return interceptor;}
}
分页查询示例:
public Page<User> getUsersByPage(int pageNum, int pageSize, UserQuery query) {Page<User> page = new Page<>(pageNum, pageSize);LambdaQueryWrapper<User> wrapper = Wrappers.lambdaQuery();wrapper.eq(query.getStatus() != null, User::getStatus, query.getStatus()).like(StringUtils.isNotBlank(query.getKeyword()), User::getName, query.getKeyword());return userMapper.selectPage(page, wrapper);
}
🚀 三、代码生成与快速开发
💡 代码生成器配置
public class CodeGenerator {public static void main(String[] args) {AutoGenerator generator = new AutoGenerator();// 数据源配置DataSourceConfig dataSourceConfig = new DataSourceConfig();dataSourceConfig.setUrl("jdbc:mysql://localhost:3306/test");dataSourceConfig.setUsername("root");dataSourceConfig.setPassword("123456");dataSourceConfig.setDriverName("com.mysql.cj.jdbc.Driver");generator.setDataSource(dataSourceConfig);// 全局配置GlobalConfig globalConfig = new GlobalConfig();globalConfig.setOutputDir(System.getProperty("user.dir") + "/src/main/java");globalConfig.setAuthor("Developer");globalConfig.setOpen(false);globalConfig.setSwagger2(true);generator.setGlobalConfig(globalConfig);// 包配置PackageConfig packageConfig = new PackageConfig();packageConfig.setParent("com.example");packageConfig.setEntity("entity");packageConfig.setMapper("mapper");packageConfig.setService("service");packageConfig.setController("controller");generator.setPackageInfo(packageConfig);// 策略配置StrategyConfig strategyConfig = new StrategyConfig();strategyConfig.setNaming(NamingStrategy.underline_to_camel);strategyConfig.setColumnNaming(NamingStrategy.underline_to_camel);strategyConfig.setEntityLombokModel(true);strategyConfig.setRestControllerStyle(true);strategyConfig.setInclude("user", "order"); // 要生成的表generator.setStrategy(strategyConfig);generator.execute();}
}
⚡ 生成结果结构
src/main/java/com/example/
├── entity/ # 实体类
│ ├── User.java
│ └── Order.java
├── mapper/ # Mapper接口
│ ├── UserMapper.java
│ └── OrderMapper.java
├── service/ # 服务接口
│ ├── IUserService.java
│ └── IOrderService.java
└── controller/ # 控制器├── UserController.java└── OrderController.java
⚖️ 四、全方位对比分析
💡 核心特性对比
特性 | MyBatis | MyBatis-Plus | 优势方 |
---|---|---|---|
CRUD操作 | 手动编写 | 自动封装 | MyBatis-Plus |
条件构造 | 手动拼接 | Lambda表达式 | MyBatis-Plus |
代码生成 | 需要插件 | 官方生成器 | MyBatis-Plus |
SQL控制 | 完全控制 | 部分封装 | MyBatis |
学习曲线 | 平缓 | 稍陡峭 | MyBatis |
灵活性 | 高 | 中等 | MyBatis |
开发效率 | 低 | 高 | MyBatis-Plus |
社区生态 | 成熟稳定 | 活跃发展 | 持平 |
📊 性能对比
场景 | MyBatis | MyBatis-Plus | 说明 |
---|---|---|---|
简单CRUD | 100ms | 95ms | 差异不大 |
复杂查询 | 150ms | 160ms | MyBatis略优 |
分页查询 | 依赖插件 | 原生支持 | MyBatis-Plus更稳定 |
批量操作 | 需要手动实现 | 内置支持 | MyBatis-Plus更便捷 |
🔧 代码量对比
相同功能的实现代码量:
// MyBatis实现分页查询
public List<User> getUsersByPage(int pageNum, int pageSize, String keyword) {RowBounds rowBounds = new RowBounds((pageNum - 1) * pageSize, pageSize);return userMapper.selectUsersByKeyword(keyword, rowBounds);
}// MyBatis-Plus实现分页查询
public Page<User> getUsersByPage(int pageNum, int pageSize, String keyword) {Page<User> page = new Page<>(pageNum, pageSize);LambdaQueryWrapper<User> wrapper = Wrappers.lambdaQuery();wrapper.like(StringUtils.isNotBlank(keyword), User::getName, keyword);return userMapper.selectPage(page, wrapper);
}
🎯 五、适用场景与选型指南
💡 技术选型决策框架
🎯 具体场景建议
推荐使用 MyBatis-Plus 的场景:
- 新项目启动:快速搭建基础CRUD功能
- 管理后台系统:大量表单和列表页面
- 微服务架构:需要快速开发数据访问层
- 团队技能一般:团队成员SQL能力较弱
推荐使用 MyBatis 的场景:
- 复杂业务系统:需要复杂SQL和存储过程
- 性能敏感场景:需要精细控制SQL优化
- 遗留系统维护:基于MyBatis的现有系统
- DBA严格管控:SQL需要DBA审核的场景
📝 选型检查清单
考虑因素 | MyBatis | MyBatis-Plus |
---|---|---|
开发速度要求 | ❌ | ✅ |
SQL复杂度要求 | ✅ | ❌ |
团队学习成本 | ✅ | ❌ |
长期维护性 | ✅ | ✅ |
生态完整性 | ✅ | ✅ |
性能要求 | ✅ | ⚠️ |
🔧 六、混合使用策略
💡 共存与渐进式迁移
在实际项目中,可以采用混合使用策略:
⚡ 混合使用示例
// 传统MyBatis的Mapper
public interface UserMapper extends BaseMapper<User> {// MyBatis-Plus提供的CRUD方法// 自定义复杂查询(传统MyBatis方式)@Select("SELECT * FROM users WHERE create_time BETWEEN #{start} AND #{end}")List<User> selectByCreateTimeRange(@Param("start") Date start, @Param("end") Date end);// 使用MyBatis-Plus的条件构造器default List<User> selectByComplexCondition(UserQuery query) {LambdaQueryWrapper<User> wrapper = Wrappers.lambdaQuery();wrapper.eq(query.getStatus() != null, User::getStatus, query.getStatus()).ge(query.getStartTime() != null, User::getCreateTime, query.getStartTime()).le(query.getEndTime() != null, User::getCreateTime, query.getEndTime());return selectList(wrapper);}
}
🔧 迁移策略建议
- 初期:新功能使用 MyBatis-Plus,旧功能保持不动
- 中期:逐步将简单CRUD迁移到 MyBatis-Plus
- 后期:复杂功能评估后决定是否迁移
🔚 总结与建议
📚 核心结论
- MyBatis:适合需要精细控制SQL、复杂查询场景
- MyBatis-Plus:适合快速开发、简单CRUD为主的项目
- 混合使用:大多数现实项目的合理选择
🎯 最终建议
项目类型 | 推荐方案 | 理由 |
---|---|---|
全新项目 | MyBatis-Plus | 开发效率高,功能全面 |
复杂系统 | MyBatis | SQL控制精细,性能优化空间大 |
迁移项目 | 混合策略 | 平衡效率与风险 |
微服务 | MyBatis-Plus | 快速迭代,标准统一 |