MapResultHandler 处理程序,将结果集放入 HashMap
public class MapResultHandler implements ResultHandler {private final Map mappedResults = new HashMap();@Overridepublic void handleResult(ResultContext context) {@SuppressWarnings("rawtypes")Map map = (Map)context.getResultObject();mappedResults.put(map.get("key"), map.get("value"));}public Map getMappedResults() {return mappedResults;}}
示例 Mapper
@Mapper@Repository public interface BaseUnitMapper {// 收费标准绑定 区域楼宇单元房号 - 房屋IDMap<String, Long> getHouseMapByAreaId(@Param("areaId") Long areaId);}
示例 Mapper.xml<select id="getHouseMapByAreaId" resultMap="mapResultLong">SELECTCONCAT( h.bulid_area_name, h.build_name, h.unit_name, h.house_num ) k,h.house_id vFROMbase_house hWHEREh.area_id = ##{areaId}GROUP BYh.house_id</select><resultMap id="mapResultLong" type="java.util.HashMap"><result property="key" column="k" javaType="string" jdbcType="VARCHAR"/><result property="value" column="v" javaType="long" jdbcType="INTEGER"/></resultMap>
之后在代码中调用 SessionMapper 类对应的方法即可 。
使用 values 批量插入MySQL insert 语句支持使用 values (),(),() 的方式一次插入多行数据,通过 mybatis foreach 结合 java 集合可以实现批量插入,代码写法如下:
<insert id="insertList">insert into table(colom1, colom2)values<foreach collection="list" item="item" index="index" separator=",">( ##{item.colom1}, ##{item.colom2})</foreach></insert>
使用 EasyPOI 读写 Excelhttp://doc.wupaas.com/docs/easypoi/easypoi-1c0u4mo8p4ro8 采用基于注解的导入导出,修改注解就可以修改Excel,非常方便,代码维护起来也容易 。
3. 第三版:EasyExcel + 缓存数据库查询操作 + 批量插入第二版采用 EasyPOI 之后,对于几千、几万的 Excel 数据已经可以轻松导入了,不过耗时有点久(5W 数据 10分钟左右写入到数据库)不过由于后来导入的操作基本都是开发在一边看日志一边导入,也就没有进一步优化 。但是好景不长,有新小区需要迁入,票据 Excel 有 41w 行,这个时候使用 EasyPOI 在开发环境跑直接就 OOM 了,增大 JVM 内存参数之后,虽然不 OOM 了,但是 CPU 占用 100% 20 分钟仍然未能成功读取全部数据 。故在读取大 Excel 时需要再优化速度 。莫非要我这个渣渣去深入 POI 优化了吗?别慌,先上 Github 找找别的开源项目 。这时阿里 EasyExcel 映入眼帘:
文章插图
图片
emmm,这不是为我量身定制的吗!赶紧拿来试试 。EasyExcel 采用和 EasyPOI 类似的注解方式读写 Excel,因此从 EasyPOI 切换过来很方便,分分钟就搞定了 。也确实如阿里大神描述的:41w行、25列、45.5m 数据读取平均耗时 50s,因此对于大 Excel 建议使用 EasyExcel 读取 。
4. 第四版:优化数据插入速度在第二版插入的时候,我使用了 values 批量插入代替逐行插入 。每 30000 行拼接一个长 SQL、顺序插入 。整个导入方法这块耗时最多,非常拉跨 。后来我将每次拼接的行数减少到 10000、5000、3000、1000、500 发现执行最快的是 1000 。结合网上一些对 innodb_buffer_pool_size 描述我猜是因为过长的 SQL 在写操作的时候由于超过内存阈值,发生了磁盘交换 。关注公号:码猿技术专栏,回复关键词:1111 获取阿里内部Java调优手册;限制了速度,另外测试服务器的数据库性能也不怎么样,过多的插入他也处理不过来 。所以最终采用每次 1000 条插入 。
每次 1000 条插入后,为了榨干数据库的 CPU,那么网络IO的等待时间就需要利用起来,这个需要多线程来解决,而最简单的多线程可以使用 并行流 来实现,接着我将代码用并行流来测试了一下:
10w行的 excel、42w 欠单、42w记录详情、2w记录、16 线程并行插入数据库、每次 1000 行 。插入时间 72s,导入总时间 95 s 。
文章插图
图片
并行插入工具类并行插入的代码我封装了一个函数式编程的工具类,也提供给大家
/** * 功能:利用并行流快速插入数据 * * @author Keats * @date 2020/7/1 9:25 */public class InsertConsumer {/*** 每个长 SQL 插入的行数,可以根据数据库性能调整*/private final static int SIZE = 1000;/*** 如果需要调整并发数目,修改下面方法的第二个参数即可*/static {System.setProperty("java.util.concurrent.ForkJoinPool.common.parallelism", "4");}/*** 插入方法** @param list插入数据集合* @param consumer 消费型方法,直接使用 mapper::method 方法引用的方式* @param <T>插入的数据类型*/public static <T> void insertData(List<T> list, Consumer<List<T>> consumer) {if (list == null || list.size() < 1) {return;}List<List<T>> streamList = new ArrayList<>();for (int i = 0; i < list.size(); i += SIZE) {int j = Math.min((i + SIZE), list.size());List<T> subList = list.subList(i, j);streamList.add(subList);}// 并行流使用的并发数是 CPU 核心数,不能局部更改 。全局更改影响较大,斟酌streamList.parallelStream().forEach(consumer);}}
推荐阅读
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 我们市场上面遇到的价值上千万级别的钱币,遇到最好绕道走
- 相机sd卡数据恢复
- 百万级别的天空蓝翡翠,究竟长什么样子?
- 如何减少数据中心网络拥塞
- 数据中心网络拥塞的原因及解决办法
- 值得与Python结合使用的五个新颖的数据科学工具
- 数据库透明加密技术大总结
- 数据中心末端配电的数字化方案
- AI和大数据是一对 “孪生兄弟”
- 企业数据加密系统