MybatisPlus SQL 注入器实现高性能批量插入(MySQL Oracle)
一、SQL 注入器是什么在上文《MybatisPlus 构造器 wrapper 的使用与原理》中我们讲解过 MybatisPlus 的核心运行流程获取语法模板 SQL代入表及字段信息代入条件构造器逻辑其中第一步获取 SQL 语法模板是整个执行流程的基础。MybatisPlus 内置了大量通用 SQL 模板能够满足绝大多数常规业务需求。但在大型项目、复杂业务场景下内置的原生模板数量有限无法覆盖所有特殊语法场景。如果仅少量 SQL 需要特殊语法我们可以手动编写 XML 文件解决但如果大量业务 SQL 需要复用同一套特殊模板逐个手写 XML 会极度冗余、低效、不利于统一维护。而MybatisPlus SQL 注入器就是为解决该问题诞生的核心拓展机制允许开发者自定义全局 SQL 模板、注入到 MybatisPlus 原生流程中全局复用、无需重复编写 SQL大幅简化开发、统一代码规范、提升执行性能。二、批量插入的性能问题2.1 saveBatch 的运行原理日常开发中我们通常使用 MybatisPlus 自带的saveBatch方法实现数据批量插入。深入源码可以发现原生 saveBatch 并不是真正意义上的批量插入。其底层本质是for 循环逐条插入默认每 1000 条数据执行一次flush刷新提交。虽然该方法复用了同一个sqlSession减少了部分链接创建开销比逐条单独提交性能更高但循环单条插入依然存在大量 SQL 交互、数据库频繁解析执行语句的问题大批量数据插入时性能瓶颈极其明显无法满足大数据量导入、同步、初始化等业务场景。2.2 理想的批量插入形式数据库原生支持拼接多值的批量插入语法一次请求写入多条数据极大减少数据库交互次数是性能最优的插入方式。MySQL 标准批量插入语法INSERT INTO your_table_name (column1, column2, column3) VALUES (value1a, value2a, value3a), (value1b, value2b, value3b), (value1c, value2c, value3c);Oracle 标准批量插入语法INSERT ALL INTO table_name (column1, column2, ...) VALUES (value1a, value2a, ...) INTO table_name (column1, column2, ...) VALUES (value1b, value2b, ...) INTO table_name (column1, column2, ...) VALUES (value1c, value2c, ...) SELECT * FROM dual;想要极致提升批量插入性能就需要通过SQL 注入器将数据库原生批量插入语法封装为 MybatisPlus 全局通用方法。三、构造批量插入的 SQL 注入器3.1 自定义 Oracle 批量插入方法MybatisPlus 已内置 MySQL 高性能批量插入方法InsertBatchSomeColumn无需自定义但未适配 Oracle 数据库因此我们需要手动编写 Oracle 批量插入拓展方法继承AbstractMethod实现 SQL 注入。public class InsertBatchSomeColumnOracle extends AbstractMethod { // Oracle 批量插入SQL模板 private static String ORACLE_INSERT_SQL_MODE script\nINSERT ALL\n %s \n SELECT 1 FROM DUAL/script; // 字段过滤断言 Setter Accessors(chain true) private PredicateTableFieldInfo predicate; public InsertBatchSomeColumnOracle() { super(insertBatchSomeColumnOracle); } Override public MappedStatement injectMappedStatement(Class? mapperClass, Class? modelClass, TableInfo tableInfo) { KeyGenerator keyGenerator new NoKeyGenerator(); ListTableFieldInfo fieldList tableInfo.getFieldList(); // 拼接插入字段 String insertSqlColumn tableInfo.getKeyInsertSqlColumn(true, false) this.filterTableFieldInfo(fieldList, predicate, TableFieldInfo::getInsertSqlColumn, EMPTY); String columnScript LEFT_BRACKET insertSqlColumn.substring(0, insertSqlColumn.length() - 1) RIGHT_BRACKET; // 拼接字段属性值 String insertSqlProperty tableInfo.getKeyInsertSqlProperty(true, ENTITY_DOT, false) this.filterTableFieldInfo(fieldList, predicate, i - i.getInsertSqlProperty(ENTITY_DOT), EMPTY); insertSqlProperty LEFT_BRACKET insertSqlProperty.substring(0, insertSqlProperty.length() - 1) RIGHT_BRACKET; // 组装单条插入语句 String oracleModelInsertSql INTO tableInfo.getTableName() columnScript VALUES insertSqlProperty; // 遍历集合批量拼接 String valuesScript SqlScriptUtils.convertForeach(oracleModelInsertSql, list, null, ENTITY, ); String keyProperty null; String keyColumn null; // 主键逻辑处理自增/序列主键 if (tableInfo.havePK()) { if (tableInfo.getIdType() IdType.AUTO) { keyGenerator new Jdbc3KeyGenerator(); keyProperty tableInfo.getKeyProperty(); keyColumn tableInfo.getKeyColumn(); } else { if (null ! tableInfo.getKeySequence()) { TableInfoHelper.genKeyGenerator(insertBatchSomeColumnOracle, tableInfo, builderAssistant); keyProperty tableInfo.getKeyProperty(); keyColumn tableInfo.getKeyColumn(); } } } // 最终拼接完整SQL String sql String.format(ORACLE_INSERT_SQL_MODE, valuesScript); SqlSource sqlSource languageDriver.createSqlSource(configuration, sql, modelClass); return this.addInsertMappedStatement(mapperClass, modelClass, insertBatchSomeColumnOracle, sqlSource, keyGenerator, keyProperty, keyColumn); } }3.2 自定义 SQL 注入器注册拓展方法继承DefaultSqlInjector将 MySQL、Oracle 两种高性能批量插入方法注册到 MybatisPlus 全局容器中实现全局生效。public class CustomerSqlInjector extends DefaultSqlInjector { Override public ListAbstractMethod getMethodList(Class? mapperclass, TableInfo tableInfo) { ListAbstractMethod methodList super.getMethodList(mapperclass, tableInfo); // 注入MySQL高性能批量插入方法 methodList.add(new InsertBatchSomeColumn()); // 注入Oracle高性能批量插入方法 methodList.add(new InsertBatchSomeColumnOracle()); return methodList; } }3.3 自定义通用 Mapper 接口对外调用入口创建通用父 Mapper封装拓展的批量插入方法所有业务 Mapper 继承该接口即可直接使用。public interface CustomerMapperT extends BaseMapperT { // MySQL高性能批量插入 void insertBatchSomeColumn(Param(list) ListT list); // Oracle高性能批量插入 void insertBatchSomeColumnOracle(Param(list) ListT list); }可按需拓展 Service 层接口适配业务层调用public interface CustomerServiceT extends IServiceT { void insertBatchSomeColumn(ListT list); void insertBatchSomeColumnOracle(ListT list); }3.4 启用自定义注入器业务 Mapper 继承自定义父接口Mapper public interface CsdnUserInfoMapper extends CustomerMapperCsdnUserInfo { }yml 配置文件指定自定义 SQL 注入器mybatis-plus: global-config: sql-injector: com.example.MyLogicSqlInjector四、总结本文详细剖析了MybatisPlus 原生批量插入的性能缺陷深入讲解了SQL 注入器的核心原理并完整实现了适配 MySQL、Oracle 的高性能全局批量插入方案。原生saveBatch底层为循环单条插入大批量数据场景性能较差无法满足高性能业务需求SQL 注入器是 MybatisPlus 强大的拓展机制支持自定义全局 SQL 模板无需重复编写 XML代码复用性、可维护性极高通过自定义注入方法适配不同数据库原生批量插入语法一次请求批量写入多条数据大幅减少数据库 IO 交互数据批量插入性能提升数倍该方案全局生效所有业务 Mapper 只需继承通用接口即可直接使用适配所有业务场景是企业级项目标准的批量插入优化方案。掌握 SQL 注入器 的使用不仅可以解决批量插入性能问题还可以自定义查询、更新、删除等通用 SQL 模板极大提升开发效率是深入掌握 MybatisPlus 高级特性、进行项目性能优化的核心技能。以上就是今天的分享觉得有帮助记得点赞收藏以上所说到的漏洞扫描工具都整理成压缩包了朋友们如果需要可以微信扫描下方CSDN官方认证二维码免费领取【保证100%免费】同时还为准备学习网络安全黑客或者正在学习整理了一套360独家内部资料包含以下内容如有需要也可文末获取①网络安全学习路线②20份渗透测试电子书③安全攻防357页笔记④50份安全攻防面试指南⑤安全红队渗透工具包⑥网络安全必备书籍⑦100个漏洞实战案例⑧安全大厂内部视频资源⑨历年CTF夺旗赛题解析一、网络安全黑客学习路线网络安全黑客学习路线形成网络安全领域所有的知识点汇总它的用处就在于你可以按照上面的知识点去找对应的学习资源保证自己学得较为全面。二、网络安全教程视频我们在看视频学习的时候不能光动眼动脑不动手比较科学的学习方法是在理解之后运用它们这时候练手项目就很适合了。三、网络安全CTF实战案例光学理论是没用的要学会跟着一起敲要动手实操才能将自己的所学运用到实际当中去这里带来的是CTFSRC资料HW资料毕竟实战是检验真理的唯一标准嘛~四、网络安全面试题最后我们所有的作为都是为就业服务的所以关键的临门一脚就是咱们的面试题内容所以面试题板块是咱们不可或缺的部分这里我给大家准备的就是我在面试期间准备的资料。网安其实不难难的是坚持和相信自己我的经验是既然已经选定网安你就要相信它相信它能成为你日后进阶的高效渠道这样自己才会更有信念去学习才能在碰到困难的时候坚持下去。机会属于有准备的人这是一个实力的时代。人和人之间的差距不在于智商而在于如何利用业余时间只要你想学习什么时候开始都不晚不要担心这担心那你只需努力剩下的交给时间这份完整版的网络安全学习资料已经上传CSDN朋友们如果需要可以微信扫描下方CSDN官方认证二维码免费领取【保证100%免费】