使用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秒