美文网首页从零开始学springboot随笔-生活工作点滴
29.从零开始学springboot-批量“覆盖导入”&

29.从零开始学springboot-批量“覆盖导入”&

作者: 码哥说 | 来源:发表于2019-07-15 09:26 被阅读25次

前言

作者之前做了个excel导入导出的功能。之前的实现思路是完全的"覆盖导入"。什么意思?意思就是,每次导入excel时按照一定的过滤条件,
把之前存储的数据的状态置为删除状态(在真实的环境中开发,几乎不存在"物理删除",也就是真正的把数据删除的应用场景,基本上都是用一个"状态"字段来控制)。
然后,本次导入的数据就无需关心之前的那些数据,只需按照"新增"插入记录即可。

这种实现方式有好处也有坏处。
好处:
屏蔽了很多的"细节",所有的数据可以批量插入,简单高效率。

坏处:
屏蔽的"细节"会引起很多关联性的逻辑问题。
打个比方,比如你excel导入了一批数据,执行了批量入库操作,之后mysql会自动为每条记录生成一个唯一的主键ID,此后,你用这个唯一的ID去关联了一些业务上的东西。
此时,如果你采用"覆盖导入"的方式再次导入一摸一样的excel,就会出现什么问题呢?是的,之前数据库的那批记录全部是"删除"状态了,相同的一条记录生成了另一个主键ID,这时,
业务系统就会找不到之前关联的那条记录。

所以,针对业务强关联的系统,有必要思考一种真正的"更新导入"的方式。

思考

为了解决"覆盖导入"引发的业务逻辑问题,我们需要理解"更新导入"所需要实现的几个点。

我们假设这样的业务场景:用户对同一张excel连续导入了多次,此时,正确的实现中,我们数据库中应该永远只有一份完整的导入数据。

而应对多次导入维护的这一份完整的数据,我们需要解决以下几个问题:

  • 同一份excel,用户没有修改任何记录,总记录条数不变

  • 同一份excel,用户修改了某条记录的某值,总记录条数不变

  • 同一份excel,用户新增多条记录,总记录条数增加

  • 同一份excel,用户删除了多条记录,总记录条数减少

  • 同一份excel,用户删除了多条记录,并新增了多条记录,总记录条数对应变化

以上大致描述了这些场景,大家肯定发现了,针对这些场景,我们几乎只能通过用户导入excel时,我们与库中存储的每一条记录进行比对来确定对应的操作。
是的,作者也是这样想的。所以基于"比对"的核心实现思想,我们就需要对excel的格式有一定要求,至少需要excel中有一列的值是唯一的,打个比方,execl中
每条记录都有一个编码值,这个值在现实业务里是唯一的。

基于此,我们来整理一下实现思路:

  • 导入的excel中,某条记录的编码值和数据库的某条是一致的,此时执行update操作
  • 导入的excel中,某条记录的编码值在数据库中不存在,此时执行insert操作
  • 导入的excel中,某条记录的编码值在数据库中存在,但是excel中不存在,此时执行delete操作(其实是update操作,将其置为"删除")

好的,虽然我们称这种实现方式为"更新导入",但是可以看出,此"更新"包含了新增,更新,删除(更新)操作.

我们理清了"更新导入"的实现,此时,有同学肯定有问题了,如果excel中没有这个所谓的业务编码唯一值,那么应该如何做"更新导入"呢?

作者,只能说事情很糟糕了,为什么?那意味着你只能对每条记录的每个字段进行比对,来确认该记录是更新还是删除还是新增,这无疑对性能是个打击。要是excel的字段很多,无疑很是致命。

实现

每次上传时,读取excel的记录数,同时读取数据库中已有的记录数,取两个集合的交集,差集

假设A集合为excel读取的记录,B集合读取的为数据库已有的数据

  • A和B的交集,即A和B都有的数据,就是需要更新的数据
  • A和B的差集,即A比B多出的数据,就是需要新增的数据
  • B和A的差集,即B比A多出的数据,就是需要删除的数据

为此,我们写两个函数,用来提取交集和差集。假设每一条记录的实体类为Entity,唯一的编码属性为code。

/**
 * 获取两个ArrayList的交集
 */
private List<Entity> sameList(List<Entity> oldArrayList, List<Entity> newArrayList) {
    List<Entity> resultList = oldArrayList.stream()
            .filter(item -> newArrayList.stream().map(e -> e.getCode())
                    .collect(Collectors.toList()).contains(item.getCode()))
            .collect(Collectors.toList());
    return resultList;
}

/**
 * 获取两个ArrayList的差集
 */
private List<Entity> diffList(List<Entity> firstArrayList, List<Entity> secondArrayList) {
    List<Entity> resultList = firstArrayList.stream()
            .filter(item -> !secondArrayList.stream().map(e -> e.getCode())
                    .collect(Collectors.toList()).contains(item.getCode()))
            .collect(Collectors.toList());
    return resultList;
}

总结

这边就不贴其它的代码了,关键的两个函数已经给出,思路也理清了~希望能帮到需要的同学。

请关注我的订阅号

订阅号.png

相关文章

网友评论

    本文标题:29.从零开始学springboot-批量“覆盖导入”&

    本文链接:https://www.haomeiwen.com/subject/rwnohctx.html