MybatisPlus自定义insertBatchSomeColumn实现真正批量插入(Oracle)

这篇具有很好参考价值的文章主要介绍了MybatisPlus自定义insertBatchSomeColumn实现真正批量插入(Oracle)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

项目由于插入数据量比较大,导致前端请求超时,查看系统代码发现是通过循环单条插入,因此准备先从输入批量写入开始优化,通过查询资料发现有几种方式:

MybatisPlus批量插入实现方式

1. 通过实现MybatisPlus IService接口,获取saveBatch,

底层其实是单条插入

    @Transactional(
        rollbackFor = {Exception.class}
    )
    public boolean saveBatch(Collection<T> entityList, int batchSize) {
        String sqlStatement = this.getSqlStatement(SqlMethod.INSERT_ONE);
        return this.executeBatch(entityList, batchSize, (sqlSession, entity) -> {
            sqlSession.insert(sqlStatement, entity);
        });
    }

2. 通过XML手动拼接SQL实现批量插入,

缺点是每个表都要手动编写xml,优点是效率较高

// mapper.xml
<mapper namespace="com.example.mapper.UserMapper">
    <insert id="insertUsers" parameterType="java.util.List">
        insert into user (id, name, age) values
        <foreach collection="list" item="user" separator=",">
            (#{user.id}, #{user.name}, #{user.age})
        </foreach>
    </insert>
</mapper>

3. 通过使用InsertBatchSomeColumn方法批量插入

底层也是拼接sql,但无需手动编写sql语句,效率同第二种,本文重点介绍这种方式,使用步骤:

3.1. 自定义SQL注入器实现DefaultSqlInjector,添加InsertBatchSomeColumn方法
public class MySqlInjector extends DefaultSqlInjector {

    @Override
    public List<AbstractMethod> getMethodList(Class<?> mapperClass, TableInfo tableInfo) {
        List<AbstractMethod> methodList = super.getMethodList(mapperClass, tableInfo);
        methodList.add(new InsertBatchSomeColumn(i -> i.getFieldFill() != FieldFill.UPDATE));
        return methodList;
    }
}
3.2 编写配置类,把自定义注入器放入spring容器
@Configuration
public class MybatisPlusConfig {

    @Bean
    public MySqlInjector sqlInjector() {
        return new MySqlInjector();
    }
}


3.3 编写自定义BaseMapper,加入InsertBatchSomeColumn方法
public interface MyBaseMapper<T> extends BaseMapper<T> {
    /**
     * 以下定义的 4个 method 其中 3 个是内置的选装件
     */
    int insertBatchSomeColumn(List<T> entityList);
}
3.4 需要批量插入的Mapper继承自定义BaseMapper
@Mapper
public interface StudentMapper extends MyBaseMapper<Student> {

    @SelectProvider(value = MySelectProvider.class, method = "getSql")
    Student select(String sql);
}
3.5 测试使用

不出意外,组装后的sql语句应该是下面这样

INSERT INTO student (id,name,age) VALUES    (?,?,?)  ,  (?,?,?)  ,  (?,?,?)

正常mysql数据库,做到这一步就完成了。项目采用的是Mybatis Plus3.4.2,作者被人也在注释标注了只在mysql做过测试,但是Oracle数据库的批量插入语句语法和Mysql不同,所以需要修改SQL
mybatis批量插入oracle,oracle,数据库,mybatis,sql

3.6 修改适配Oracle

先了解下,Oracle批量插入语句

INSERT ALL
INTO emp (empno, ename) VALUES (1001, '张三')
INTO emp (empno, ename) VALUES (1002, '李四')
INTO emp (empno, ename) VALUES (1003, '王五')
SELECT * FROM dual;

因此我们需要把SQL组装成这种结构,查看InsertBatchSomeColumn类,可以发现SQL组装逻辑在injectMappedStatement方法,因此我们模仿InsertBatchSomeColumn类,编写SQL组装逻辑

@NoArgsConstructor
@AllArgsConstructor
@SuppressWarnings("serial")
public class MyInsertBatchSomeColumn extends InsertBatchSomeColumn {

    @Setter
    @Accessors(chain = true)
    private Predicate<TableFieldInfo> predicate;

    private final String INSERT_BATCH_SQL="<script>\nINSERT ALL \n  %s\n</script>";

    @SuppressWarnings("Duplicates")
    @Override
    public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {
        //pojo类型为Map时禁用
        if (tableInfo.getEntityType().equals(Map.class)) {
            return null;
        }
        //return super.injectMappedStatement(mapperClass,modelClass,tableInfo);
        KeyGenerator keyGenerator = new NoKeyGenerator();
        SqlMethod sqlMethod = SqlMethod.INSERT_ONE;
        List<TableFieldInfo> fieldList = tableInfo.getFieldList();
        String insertSqlColumn = tableInfo.getKeyInsertSqlColumn(false) +
                this.filterTableFieldInfo(fieldList, predicate, TableFieldInfo::getInsertSqlColumn, EMPTY);
        String columns = insertSqlColumn.substring(0, insertSqlColumn.length() - 1) ;
        String insertSqlProperty = tableInfo.getKeyInsertSqlProperty(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 valuesScript = convertForeach(insertSqlProperty, "list", tableInfo.getTableName(),columns, ENTITY, NEWLINE);
        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()) {
                    keyGenerator = TableInfoHelper.genKeyGenerator(getMethod(sqlMethod), tableInfo, builderAssistant);
                    keyProperty = tableInfo.getKeyProperty();
                    keyColumn = tableInfo.getKeyColumn();
                }
            }
        }
        String sql = String.format(INSERT_BATCH_SQL, valuesScript);
        SqlSource sqlSource = languageDriver.createSqlSource(configuration, sql, modelClass);
        return this.addInsertMappedStatement(mapperClass, modelClass, getMethod(sqlMethod), sqlSource, keyGenerator, keyProperty, keyColumn);
    }
    public static String convertForeach(final String sqlScript, final String collection, final String tableName,final String columns, final String item, final String separator) {
        StringBuilder sb = new StringBuilder("<foreach");

        if (StringUtils.isNotBlank(collection)) {
            sb.append(" collection=\"").append(collection).append("\"");
        }

        if (StringUtils.isNotBlank(item)) {
            sb.append(" item=\"").append(item).append("\"");
        }

        if (StringUtils.isNotBlank(separator)) {
            sb.append(" separator=\"").append(separator).append("\"");
        }

        sb.append(">").append("\n");

        if (StringUtils.isNotBlank(tableName)) {
            sb.append(" INTO ").append(tableName).append(" ");
        }

        if (StringUtils.isNotBlank(columns)) {
            sb.append(LEFT_BRACKET).append(columns).append(RIGHT_BRACKET).append(" VALUES ");
        }

        return sb.append(sqlScript).append("\n").append("</foreach>\n").append(" SELECT ").append("*").append(" FROM dual").toString();
    }
}

修改3.1中方法

public class MySqlInjector extends DefaultSqlInjector {

    @Override
    public List<AbstractMethod> getMethodList(Class<?> mapperClass, TableInfo tableInfo) {
        List<AbstractMethod> methodList = super.getMethodList(mapperClass, tableInfo);
        //这里改成我们自己的实现MyInsertBatchSomeColumn
        methodList.add(new MyInsertBatchSomeColumn(i -> i.getFieldFill() != FieldFill.UPDATE));
        return methodList;
    }
}

到此,我们就完成了Oracle的批量插入,代码改动较小,下一步如果速度还是不满足要求,考虑使用多线程来优化。

4.补充

经过实际测试,MybatisPlus的InsertBatchSomeColumn插件进行批量插入时并不会自动分组插入,而是你传入多少数据就全部拼接成一整条SQL,对于表结构字段多,数据量大的操作效率反而不如单条插入,原因是SQL过长虽然避免了多次网络IO,但是数据库解析SQL效率降低,因此需要对数据进行分批插入,选择合适的批次大小才能提升效率。不同表分的批次应该不同,因为字段数量不同;字段数量少的批次可以适当放大,字段数量多的则批次减少。文章来源地址https://www.toymoban.com/news/detail-597385.html

到了这里,关于MybatisPlus自定义insertBatchSomeColumn实现真正批量插入(Oracle)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Java8 实现批量插入和更新,SpringBoot实现批量插入和更新,Mybatis实现批量插入和更新

    基于mybatis实现的批量插入和更新 由于直接执行批量所有数据可能会出现长度超出报错问题,使用如下方式即可解决 原理还是分配执行,这里的100就是设定每次执行最大数 这里使用插入作为例子,也可以使用批量更新 更新的写法

    2024年02月12日
    浏览(54)
  • Mybatis 中传入List实现 批量插入、批量更新、批量删除

    个人收藏使用 文章来自Mybatis 中传入List实现 批量插入、批量更新、批量删除 - chelsey3tsf - 博客园 (cnblogs.com) 1. 批量插入 : Mapper层: 对应的mapper.xml: 如果List数据量比较大,可以考虑将List分批次插入 2. 批量更新: 批量更新只提供更新单个字段的,因为更新多个字段无论哪种

    2024年02月11日
    浏览(58)
  • java实现批量插入数据

    日常工作或者学习中,可能会遇到批量插入数据的需求,一般情况下数据量少的时候,我们会直接调用批量接口插入数据即可,当数据量特别大时,可能由于数据库限制,插入的数据最多不能超过100条(假如限制100条),就算是数据库支持一次性插入千条也会耗内存,如果使用

    2024年02月11日
    浏览(54)
  • 使用saveOrUpdateBatch实现批量插入更新数据

    saveOrUpdateBatch 是 Hibernate 中的一个方法,可以用来批量插入或更新数据。这个方法的参数是一个 List,里面可以存储多个实体对象。当 Hibernate 执行这个方法时,会检查每个实体对象是否存在主键,如果存在主键就执行 update 操作,否则执行 insert 操作。 使用 saveOrUpdateBatch 的代

    2024年02月11日
    浏览(75)
  • 【数据库】Python脚本实现数据库批量插入事务

    在工作中可能会遇到需要批量插入的场景, 而批量插入的过程具有 耗时长 的特点, 再此过程很容易出现程序崩溃的情况.为了解决插入大量数据插入后崩溃导致 已插入数据无法清理 及 未插入数据无法筛出 的问题, 需要编写一个脚本记录已插入和未插入的数据, 并可以根据记录

    2024年02月08日
    浏览(49)
  • Mysql 实现批量插入对已存在数据忽略或更新

    对已存在的数据进行 忽略/更新 ,需要唯一索引/主键。 唯一索引可为多个字段的联合索引,比如根据我提供的sql中,我需要``name + age`不重复,则可把这2个字段联合创建为唯一索引 创建联合唯一索引的sql 批量插入对已存在数据忽略 批量插入对已存在数据更新 笔者这里只举

    2024年02月15日
    浏览(47)
  • <JDBC> 批量插入 的四种实现方式:你真的get到了吗?

    🛒 本文收录与专栏 :《JDBC》专栏 📢 专栏目的是解释JDBC的关键点,与各位一路同行,会持续输出,欢迎免费订阅!! ✨总有人间一两风,填我十万八千梦✨ 批量插入就是 批量执行SQL语句 当需要成批插入或者更新记录时,可以采用Java的批量更新机制,这一机制允许多条语

    2024年02月02日
    浏览(49)
  • Mybatis-Plus的SQL注入器实现批量插入/修改,效率比较

    mysql支持一条sql语句插入多条数据。但是Mybatis-Plus中默认提供的saveBatch、updateBatchById方法并不能算是真正的批量语句,而是遍历实体集合执行INSERT_ONE、UPDATE_BY_ID语句。 mybatis-plus虽然做了分批请求、一次提交的处理。但如果jdbc不启用配置rewriteBatchedStatements,那么批量提交的s

    2024年02月11日
    浏览(50)
  • MyBatisPlus 批量添加

    一般来说,批量插入可以使用 MyBatisPlus 中 ServiceImpl 自带的方法 saveBatch 打开 sql 日志,application.yml 添加配置,mapper-locations 配置 mapper 路径 可以发现插入是在同一个 SqlSession,但并不是理想中的批量插入 它的插入算法我没有细究,但从日志观察可以看出它的插入条数是无序的

    2024年02月04日
    浏览(50)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包