2026年了,还在手写SQL?我整理了5个让MyBatis-plus失效的场景。

📅 2026/6/26 15:26:04
2026年了,还在手写SQL?我整理了5个让MyBatis-plus失效的场景。
团队去年全面切MyBatis-plus想着CRUD不用写了效率翻倍。结果半年后复杂业务场景里还是手写SQL而且比原来更绕。整理了5个让MyBatis-plus失效的场景不是框架不好是用错了地方。场景一多层嵌套子查询MyBatis-plus的QueryWrapper链式调用两层嵌套还能看三层以上就是灾难// 两层勉强能读 queryWrapper.eq(status,1).in(id, new QueryWrapperOrder().select(user_id).eq(amount,100));// 三层我放弃了直接写XML实际业务查最近30天消费超过500元且购买过指定品类且退货率低于10%的用户。三层子查询聚合条件QueryWrapper写出来像天书维护的人骂娘。手写SQLselectidfindQualifiedUsersresultTypeUserSELECT u.* FROM user u WHERE u.id IN(SELECT o.user_id FROMordero WHERE o.create_timeDATE_SUB(NOW(), INTERVAL30DAY)AND o.amount500AND o.id IN(SELECT oi.order_id FROM order_item oi WHERE oi.category_id#{categoryId})AND o.user_id NOT IN(SELECT r.user_id FROM refund r WHERE r.rate0.1))/select看着长但结构清晰SQL优化器也能看懂。MyBatis-plus生成的SQL三层嵌套后逻辑混乱执行计划全表扫描。场景二动态表名/动态列分库分表场景表名按月份拆分 order_202601 、 order_202602 。MyBatis-plus的 TableName 注解不支持动态表名除非自己写拦截器替换SQL但那样还不如直接写XML。selectidstatReportresultTypeMapSELECT province, city, channel, DATE(create_time)as dt, COUNT(*)as order_cnt, SUM(amount)as total_amount, AVG(amount)as avg_amount, SUM(CASE WHENstatus9THEN1ELSE0END)/COUNT(*)as refund_rate FROMorderwhereiftestprovince ! nullAND province#{province}/ififteststartDate ! nullAND create_time#{startDate}/if/whereGROUP BY province, city, channel, DATE(create_time)WITH ROLLUP/selectWITH ROLLUP 做维度汇总MyBatis-plus不支持这种语法只能原生SQL。场景四批量插入优化MyBatis-plus的 saveBatch 默认是一条条INSERT批量1000条数据数据库往返1000次。// 默认实现性能极差 userService.saveBatch(userList);//1000次网络往返想优化自己写 INSERT INTO … VALUES (…), (…), (…) 或者配置 rewriteBatchedStatementstrue 但MyBatis-plus不帮你做这个。手写SQLinsertidbatchInsertINSERT INTO user(name, email, create_time)VALUESforeachcollectionlistitemitemseparator,(#{item.name}, #{item.email}, NOW())/foreach/insert一次网络往返性能提升100倍。场景五数据库特定语法MySQL的 ON DUPLICATE KEY UPDATE 、PostgreSQL的 ON CONFLICT 、SQL Server的 MERGE MyBatis-plus的通用CRUD不支持这些方言。insertidupsertINSERT INTO user(id, name, email)VALUES(#{id}, #{name}, #{email})ON DUPLICATE KEY UPDATE nameVALUES(name), emailVALUES(email), update_timeNOW()/insert我的用法不是不用MyBatis-plus是分层用简单CRUD、单表查询、快速原型MyBatis-plus省代码复杂查询、子查询、聚合、批量、方言特性手写XML可控可优化一个反直觉的结论MyBatis-plus最大的价值不是让你不写SQL是让你明确知道哪些SQL不用写。简单场景省时间复杂场景不折腾边界清晰。如果所有SQL都手写和MyBatis有什么区别如果所有SQL都用MyBatis-plus复杂业务就是灾难。最后说个数据我们团队代码统计MyBatis-plus占60%手写XML占40%。但Bug率手写XML的部分反而更低因为复杂逻辑显式表达review时容易发现问题。MyBatis-plus生成的SQL有时候执行计划全表扫描直到线上慢查询才发现。工具是帮你省时间的不是帮你思考的。复杂场景手写SQL是负责任的选择。