关于mybatis使用foreach插入速度较慢的问题

摘要:
mybatis进行foreach的时候是没有缓存的,每次都得重新解析一下,所以越来越慢。如果想要达到上图的程度,必须先解决foreach拼接问题我没有找到好的办法。但是即使拼接成字符串之后,速度可能的确会很快,但是没办法在mybatis中引用。*@returnint成功插入的行数。*/privateintbulkLoadFromInputStreamthrowsSQLException{if{logger.info("输入流为NULL,没有数据导入。*@paramcolumnName要插入数据的列名。

使用mybatis批量插入,看了这篇博客

https://blog.csdn.net/m0_37981235/article/details/79131493

关于mybatis使用foreach插入速度较慢的问题第1张

我这种懒货懒得想其中原因,直接上手第三种!

结果测试多次,发现我插入8000条数据,第一种方式只需要30秒不到,可是第三种方法却需要一分多钟。

不知道原作者是怎么实现的,可能和插入数据的多少有关,我这里是8个字段。测试后1000taio需要1.7秒,500条需要0.4秒,切割成100条100条的插入,最终8000条数据只需要1.4s.

查询资料得知

Mybatis 在解析 foreach 的时候,因为需要循环解析 #{} 之类的占位符,foreach 的集合越大,解析越慢。

mybatis进行foreach的时候是没有缓存的,每次都得重新解析一下,所以越来越慢。

如果想要达到上图的程度,必须先解决foreach拼接问题

我没有找到好的办法。如果有大佬知道还请告知。

所以我可以java中使用字符串拼接的方式。

但是即使拼接成字符串之后,速度可能的确会很快,但是没办法在mybatis中引用。

例如

insert into tb_csp_baseDataAll (
        parentArea,
        area,
        committee,
        type,
        woman_name,
        woman_id_card,
        tel,
        wid
        ) values #{string}

这种传入的string就是字符串,带有的()是在字符串内的,可能

类似于

insert into tb_csp_baseDataAll (
        parentArea,
        area,
        committee,
        type,
        woman_name,
        woman_id_card,
        tel,
        wid
        ) values "('a','b'...)"

数据库会报错,暂时没有找到好的办法解决。

如果使用存储过程的话,传入的值为为一个list是不大可能的,最理想的方法估计就是传一个json字符串,然后进行解析,似乎也是非常的麻烦,效率也不一定会很高。

使用批处理方式解决,8000条数据也是需要22秒左右。

最后查阅资料使用LOAD DATA LOCAL INFILE实现大批量插入:https://blog.csdn.net/baidu_38083619/article/details/83378885

MySQL使用LOAD DATA LOCAL INFILE从文件中导入数据比insert语句要快,MySQL文档上说要快20倍左右。
但是这个方法有个缺点,就是导入数据之前,必须要有文件,也就是说从文件中导入。这样就需要去写文件,以及文件删除等维护。某些情况下,比如数据源并发的话,还会出现写文件并发问题,很难处理。
那么有没有什么办法,可以达到同样的效率,直接从内存(IO流中)中导入数据,而不需要写文件呢?

MySQL社区提供这样一个方法:setLocalInfileInputStream(),此方法位于com.mysql.jdbc.PreparedStatement 类中。通过使用MySQLJDBC 的setLocalInfileInputStream 方法实现从Java InputStream中load data local infile 到MySQL数据库中。

packagecom.akb.hfcx.csp.utils;

importjava.io.ByteArrayInputStream;
importjava.io.IOException;
importjava.io.InputStream;
importjava.sql.Connection;
importjava.sql.PreparedStatement;
importjava.sql.SQLException;

importjavax.annotation.Resource;

importorg.slf4j.Logger;
importorg.slf4j.LoggerFactory;
importorg.springframework.jdbc.core.JdbcTemplate;
importorg.springframework.stereotype.Component;

@Component
public classLoadDataInFileUtil {
 
    private Logger logger = LoggerFactory.getLogger(LoadDataInFileUtil.class);
    private Connection conn = null;
    @Resource
    privateJdbcTemplate jdbcTemplate;
 
    /*-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --*/
 
    /*** 将数据从输入流加载到MySQL。
     *
     * @paramloadDataSql  SQL语句。
     * @paramdataStream   输入流。
     * @paramjdbcTemplate JDBC。
     * @returnint         成功插入的行数。
     */
    private intbulkLoadFromInputStream(String loadDataSql,
                                        InputStream dataStream,
                                        JdbcTemplate jdbcTemplate) throwsSQLException {
        if (null ==dataStream) {
            logger.info("输入流为NULL,没有数据导入。");
            return 0;
        }
        conn =jdbcTemplate.getDataSource().getConnection();
        PreparedStatement statement =conn.prepareStatement(loadDataSql);
        int result = 0;
        if (statement.isWrapperFor(com.mysql.jdbc.Statement.class)) {
            com.mysql.jdbc.PreparedStatement mysqlStatement = statement.unwrap(com.mysql.jdbc.PreparedStatement.class);
            mysqlStatement.setLocalInfileInputStream(dataStream);
            result =mysqlStatement.executeUpdate();
        }
        returnresult;
    }
 
    /*-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --*/
 
    /*** 组装 SQL 语句。
     *
     * @paramdataBaseName 数据库名。
     * @paramtableName    表名。
     * @paramcolumnName   要插入数据的列名。
     */
    publicString assembleSql(String dataBaseName, String tableName, String columnName[]) {
        String insertColumnName = StringUtils.join(columnName, ",");
        String sql = "LOAD DATA LOCAL INFILE 'sql.csv' IGNORE INTO TABLE " + dataBaseName + "." + tableName + "(" + insertColumnName + ")";
        returnsql;
    }
 
    /*-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --*/
 
    /*** 往 StringBuilder 里追加数据。
     *
     * @parambuilder StringBuilder。
     * @paramobject  数据。
     */
    public voidbuilderAppend(StringBuilder builder, String object) {
        builder.append(object);
        builder.append("	");
    }
 
    /*-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --*/
 
    /*** 往 StringBuilder 里追加一条数据的最后一个字段。
     *
     * @parambuilder StringBuilder。
     * @paramobject  数据。
     */
    public voidbuilderEnd(StringBuilder builder, Object object) {
        builder.append(object);
        builder.append("
");
    }
 
    /*-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --*/
 
    /*** 通过 LOAD DATA LOCAL INFILE 大批量导入数据到 MySQL。
     *
     * @paramsql     SQL语句。
     * @parambuilder 组装好的数据。
     */
    public intfastInsertData(String sql, StringBuilder builder) {
        int rows = 0;
        InputStream is = null;
        try{
            byte[] bytes =builder.toString().getBytes();
            if (bytes.length > 0) {
                is = newByteArrayInputStream(bytes);
                //批量插入数据。
                long beginTime =System.currentTimeMillis();
                rows =bulkLoadFromInputStream(sql, is, jdbcTemplate);
                long endTime =System.currentTimeMillis();
                logger.info("LOAD DATA LOCAL INFILE :【插入" + rows + "行数据至MySql中,耗时" + (endTime - beginTime) + "ms。】");
            }
 
        } catch(SQLException e) {
            e.printStackTrace();
        } finally{
            try{
                if (null !=is) {
                    is.close();
                }
                if (null !=conn) {
                    conn.close();
                }
            } catch (IOException |SQLException e) {
                e.printStackTrace();
            }
        }
        returnrows;
    }
}

调用部分方法:

    public void addList(List<AllBaseData>list) {
        StopWatch stopWatch = newStopWatch();
        stopWatch.start();
        StringBuilder sb = newStringBuilder();
        for(AllBaseData entity : list) {
            loadDataInFileUtil.builderAppend(sb, entity.getParentArea());
            loadDataInFileUtil.builderAppend(sb, entity.getArea());
            loadDataInFileUtil.builderAppend(sb, entity.getCommittee());
            loadDataInFileUtil.builderAppend(sb, entity.getType());
            loadDataInFileUtil.builderAppend(sb, entity.getWoman_name());
            loadDataInFileUtil.builderAppend(sb, entity.getWoman_id_card());
            loadDataInFileUtil.builderAppend(sb, entity.getTel());
            loadDataInFileUtil.builderEnd(sb, entity.getWid());
        }
        
        String sql =loadDataInFileUtil.assembleSql(DATA_BASE_NAME, TABLE_NAME, COLUMN_NAME);
        int insertRow =loadDataInFileUtil.fastInsertData(sql, sb);
        System.out.println("insert应收报表数量insertRow:"+insertRow);
        stopWatch.stop();
        System.out.println("花费时间" +stopWatch.getTotalTimeSeconds());
        
    }

且不要忘记在spring的配置文件加上

<bean id="jdbcTemplate"class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource"ref="dataSource"></property>
    </bean>

dataSource是数据库相关配置bean

测试8000条数据只需要0.214秒

免责声明:文章转载自《关于mybatis使用foreach插入速度较慢的问题》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇Perl中的默认变量$_ 和 @_iis下项目绑定ip、域名以及443端口号之后项目启动不起来,iis提示“另一个程序正在使用此文件,进程无法访问。(异常来自HRESULT:0x80070020) ”下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

Flatbuffers学习

flatbuffers简介 FlatBuffers 是一个(二进制 buffer)序列化开源库,由 Google 开源现在它支持C++, C#, C, Go, Java, Kotlin, JavaScript, Lobster, Lua, TypeScript, PHP, Python, Rust and Swift等。 官网:http://google....

spring mvc 的jpa JpaRepository数据层 访问方式汇总

本文转载:http://perfy315.iteye.com/blog/1460226和http://jishiweili.iteye.com/blog/2088265 AppleFramework在数据访问控制层采用了Spring Data作为这一层的解决方案,下面就对Spring Data相关知识作一个较为详细的描述。1.Spring Data所解决的...

JavaEE-02 JSP数据交互01

学习要点 request对象 response对象 转发与重定向 session对象 include指令 课程回顾 需求描述:编写JSP页面,计算2000—3000年中存在几个闰年。 实现分析:判断闰年的算法写在方法boolean leapYear(int year)中。 提示:闰年——能够被4整除而不能被100整除,或者能够被400整除。 JSP...

【笔记】SpringBoot+Shiro 实现简单权限管理(使用mysql数据库)

网上翻了好久 都没有SpringBoot+Shiro的入门教程 原本想看《跟我学Shiro》 然后发现这是从头开始 但是我就需要和SpringBoot整一块 不需要那么多的东西 感觉这个当参考书不错 于是东拼西凑终于整成了 把别人的教程上我用不到的都删了 该改的改 终于拿到了我理想中的效果 先是数据库部分 因为是简单的实现 就没有弄得太复杂 三部分 用户...

微信支付(公众号支付JSAPI)--转载

原文路径:https://blog.csdn.net/javaYouCome/article/details/79473743 写这篇文章的目的有2个,一是自己的项目刚开发完微信支付功能,趁热回个炉温习一下,二也是帮助像我这样对微信支付不熟悉,反复看了多天文档还是一知半解,原理都没摸清,更不要说实现了。本以为网上的微信开发教程会和“java的重写与重载”一...

C#使用SmtpClient发送邮件

原理:  例如A使用163邮箱发送邮件给B(qq邮箱)。首先A会把邮件通过SMTP(Simple Mail Transfer Protocol)协议传输到163的Smtp服务器上,163的Smtp服务器会根据B的邮箱账号,把邮件通过Smtp协议发给QQ邮箱的Smtp服务器。QQ的Smtp服务器接收到邮件消息后会将之存储在QQ邮箱的邮件存储设备上。当B登陆Q...