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

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

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

我这种懒货懒得想其中缘由,直接上手第三种!spring

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

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

查询资料得知json

 Mybatis 在解析 foreach 的时候,由于须要循环解析 #{} 之类的占位符,foreach 的集合越大,解析越慢。缓存

mybatis进行foreach的时候是没有缓存的,每次都得从新解析一下,因此愈来愈慢。mybatis

若是想要达到上图的程度,必须先解决foreach拼接问题并发

我没有找到好的办法。若是有大佬知道还请告知。app

因此我能够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 类中。经过使用 MySQL JDBC 的setLocalInfileInputStream 方法实现从Java InputStream中load data local infile 到MySQL数据库中。

package com.akb.hfcx.csp.utils; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.SQLException; import javax.annotation.Resource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.stereotype.Component; @Component public class LoadDataInFileUtil { private Logger logger = LoggerFactory.getLogger(LoadDataInFileUtil.class); private Connection conn = null; @Resource private JdbcTemplate jdbcTemplate; /*-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --*/
 
    /** * 将数据从输入流加载到MySQL。 * * @param loadDataSql SQL语句。 * @param dataStream 输入流。 * @param jdbcTemplate JDBC。 * @return int 成功插入的行数。 */
    private int bulkLoadFromInputStream(String loadDataSql, InputStream dataStream, JdbcTemplate jdbcTemplate) throws SQLException { 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(); } return result; } /*-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --*/
 
    /** * 组装 SQL 语句。 * * @param dataBaseName 数据库名。 * @param tableName 表名。 * @param columnName 要插入数据的列名。 */
    public String 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 + ")"; return sql; } /*-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --*/
 
    /** * 往 StringBuilder 里追加数据。 * * @param builder StringBuilder。 * @param object 数据。 */
    public void builderAppend(StringBuilder builder, String object) { builder.append(object); builder.append("\t"); } /*-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --*/
 
    /** * 往 StringBuilder 里追加一条数据的最后一个字段。 * * @param builder StringBuilder。 * @param object 数据。 */
    public void builderEnd(StringBuilder builder, Object object) { builder.append(object); builder.append("\n"); } /*-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --*/
 
    /** * 经过 LOAD DATA LOCAL INFILE 大批量导入数据到 MySQL。 * * @param sql SQL语句。 * @param builder 组装好的数据。 */
    public int fastInsertData(String sql, StringBuilder builder) { int rows = 0; InputStream is = null; try { byte[] bytes = builder.toString().getBytes(); if (bytes.length > 0) { is = new ByteArrayInputStream(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(); } } return rows; } }

调用部分方法:

public void addList(List<AllBaseData> list) { StopWatch stopWatch = new StopWatch(); stopWatch.start(); StringBuilder sb = new StringBuilder(); 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秒

相关文章
相关标签/搜索