MybatisPlus----SQL注入器提升批量插入性能,从零基础到精通,收藏这篇就够了!

.markdown-body pre,.markdown-body pre>code.hljs{color:#333;background:#f8f8f8}.hljs-comment,.hljs-quote{color:#998;font-style:italic}.hljs-keyword,.hljs-selector-tag,.hljs-subst{color:#333;font-weight:700}.hljs-literal,.hljs-number,.hljs-tag .hljs-attr,.hljs-template-variable,.hljs-variable{color:teal}.hljs-doctag,.hljs-string{color:#d14}.hljs-section,.hljs-selector-id,.hljs-title{color:#900;font-weight:700}.hljs-subst{font-weight:400}.hljs-class .hljs-title,.hljs-type{color:#458;font-weight:700}.hljs-attribute,.hljs-name,.hljs-tag{color:navy;font-weight:400}.hljs-link,.hljs-regexp{color:#009926}.hljs-bullet,.hljs-symbol{color:#990073}.hljs-built_in,.hljs-builtin-name{color:#0086b3}.hljs-meta{color:#999;font-weight:700}.hljs-deletion{background:#fdd}.hljs-addition{background:#dfd}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:700}

一、SQL注入器是什么?

在上次的文章《MybatisPlus 构造器wrapper的使用与原理》 的第三部分,我们讲解了 MybatisPlus 运行的一些原理

  1. 获取语法模板SQL
  2. 代入表及字段信息
  3. 代入条件构造器逻辑

如果不太熟悉的,可以再去复习一遍。不难发现这第一步,就是获取语法模板SQL,MybatisPlus源码中就有不少简单的模板,如下图。但是坦白来说,对于一些大型的项目来说,内置的SQL可能还是少了点

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

如果只是个别SQL有特殊的语法,那倒罢了,针对个别SQL写个xml文件就可以解决。然而如果有很多SQL都使用同一个模板,那我们一个个去改就比较费事了,而SQL注入器就能为我们解决这个困扰

二、批量插入的性能问题
1. saveBatch 的运行原理

我们在日常使用中,可能会使用到Iservice的 saveBatch 方法来实现批量插入

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

但是如果我们继续在源码中深入,就可以发现这个批量插入本质上是在for循环中插入,默认以1000为一批,进行一次刷新flush

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

虽然这里使用的同一个sqlSession能够提高一些效率,但毕竟是for循环内的插入,真在大批量插入时性能还是有待提升。而一个大项目中,批量插入的功能还是很常见的,本期我们就看看,如何使用 MybatisPlus的SQL注入器提升批量插入性能

2. 理想的批量插入形式

我们都知道,数据库其实提供了批量插入的SQL样式,比如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;



或者

INSERT INTO table_name (column1, column2, ...)
SELECT value1a, value2a, ...
  FROM dual
UNION ALL
SELECT value1b, value2b, ...
  FROM dual
UNION ALL
SELECT value1c, value2c, ...
  FROM dual;



使用对应数据库的批量插入样式,毫无疑问是最合适的,所以我们需要在MybatisPlus中加入这样的模板

三、构造批量插入的SQL注入器
1. 自定义方法

首先我们要自定义一个方法,因为Mysql 和 Oracle 的批量插入样式不一样,所以理论上要写两个方法,但实际上MybatisPlus 针对 Mysql 已经有了一个内置方法 InsertBatchSomeColumn,所以我们只需要针对 Oracle 自定义方法即可 继承自·AbstractMethod

public class InsertBatchSomeColumnOracle extends AbstractMethod {
    
    // oracle 批量插入的格式
    private static String ORACLE_INSERT_SQL_MODE = "<script>\nINSERT ALL\n %s \n SELECT 1 FROM DUAL</script>";

    // 字段筛选条件
    @Setter
    @Accessors(chain = true)
    private Predicate<TableFieldInfo> predicate;

    public InsertBatchSomeColumnOracle() {
        super("insertBatchSomeColumnOracle");
    }

    @Override
    public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {
        KeyGenerator keyGenerator = new NoKeyGenerator();

        List<TableFieldInfo> 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()) {
                    Keybenerator:
                    TableInfoHelper.genKeyGenerator("insertBatchSomeColumnOracle", tableInfo, builderAssistant);
                    keyProperty = tableInfo.getKeyProperty();
                    keyColumn = tableInfo.getKeyColumn();
                }
            }
        }
        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);
    }
}



2. 将方法注入 MybatisPlus

仅仅写一个类,并不能直接就使用,我们还需要把这个方法,融入 MybatisPlus 的运行时。这时就要用到另一个类,这个类要继承 DefaultSqlInjector,如下,我们把mysql 和 oracle 的两个批量插入都加入 methodList 中

public class CustomerSqlInjector extends DefaultSqlInjector {
    @Override
    public List<AbstractMethod> getMethodList(Class<?> mapperclass, TableInfo tableInfo) {
        List<AbstractMethod> methodList = super.getMethodList(mapperclass, tableInfo);
        // 真正的批量插入,添加 InsertBatchSomeColumn 方法,以下仅适用于 MySQL
        methodList.add(new InsertBatchSomeColumn());
        // 真正的批量插入,添加 InsertBatchSomeColumn 方法,以下仅适用于 oracle
        methodList.add(new InsertBatchSomeColumnOracle());
        return methodList;
    }
}



3. 创建使用方法的接口

上面的内容是把新增的方法放入MybatisPlus,下面我们还需要一个给开发者使用的入口

public interface CustomerMapper<T> extends BaseMapper<T> {

    void insertBatchSomeColumn(@Param("list") List<T> list);

    void insertBatchSomeColumnOracle(@Param("list") List<T> list);
}



当然,构建完Mapper级别,你也可以继续往上构建,比如构建自己的Service级(接口及实现类),如下

public interface CustomerService<T> extends Iservice<T> {

    void insertBatchSomeColumn(List<T> list);

    void insertBatchSomeColumnOracle(List<T> list);
}



  1. 启用该注入器 现在代码层面的配置解决了,想要使用批量插入功能的,只要让我们的Mapper接口继承CostomerMapper就可以,如下:
@Mapper
public interface CsdnUserInfoMapper extends CustomerMapper<CsdnUserInfo> {

}



然后我们还需要在配置中开启这个注入器。我们可以在yml文件中这么配置

mybatis-plus:
  global-config:
    sql-injector: com.example.MyLogicSqlInjector



四、总结

在上面的学习里,我们详细阐述了如何使用 MybatisPlus的SQL注入器提升批量插入性能,希望帮大家更深入的了解 MybatisPlus 的特性及其使用,也希望能帮助大家开阔性能优化的思路,后续我们将继续为大家讲解 MybatisPlus 的更多内容

黑客/网络安全学习包

资料目录

  1. 成长路线图&学习规划

  2. 配套视频教程

  3. SRC&黑客文籍

  4. 护网行动资料

  5. 黑客必读书单

  6. 面试题合集

因篇幅有限,仅展示部分资料,需要点击下方链接即可前往获取

CSDN大礼包:《黑客&网络安全入门&进阶学习资源包》免费分享

1.成长路线图&学习规划

要学习一门新的技术,作为新手一定要先学习成长路线图方向不对,努力白费

对于从来没有接触过网络安全的同学,我们帮你准备了详细的学习成长路线图&学习规划。可以说是最科学最系统的学习路线,大家跟着这个大的方向学习准没问题。


因篇幅有限,仅展示部分资料,需要点击下方链接即可前往获取

CSDN大礼包:《黑客&网络安全入门&进阶学习资源包》免费分享

2.视频教程

很多朋友都不喜欢晦涩的文字,我也为大家准备了视频教程,其中一共有21个章节,每个章节都是当前板块的精华浓缩


因篇幅有限,仅展示部分资料,需要点击下方链接即可前往获取

CSDN大礼包:《黑客&网络安全入门&进阶学习资源包》免费分享

3.SRC&黑客文籍

大家最喜欢也是最关心的SRC技术文籍&黑客技术也有收录

SRC技术文籍:

黑客资料由于是敏感资源,这里不能直接展示哦!

4.护网行动资料

其中关于HW护网行动,也准备了对应的资料,这些内容可相当于比赛的金手指!

5.黑客必读书单

**

**

6.面试题合集

当你自学到这里,你就要开始思考找工作的事情了,而工作绕不开的就是真题和面试题。

更多内容为防止和谐,可以扫描获取~

因篇幅有限,仅展示部分资料,需要点击下方链接即可前往获取

CSDN大礼包:《黑客&网络安全入门&进阶学习资源包》免费分享

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值