基于模版方法 快速导入Excel数据到数据库

一、目标

快速导入Excel数据到数据库

二、初始化分析

整体流程

实施人员:数据准备-》数据检查
开发人员:数据导入
实施人员:数据验证
数据初始化的过程主要是,将确定格式的数据导入数据库的过程。因为涉及实施人员,为了兼容性,文件格式采用Excel。

数据导入流程

对于开发而言数据导入分为三个流程:文件导入、文件读取,文件解析、插入数据库。接下来我们分别聊下各个流程。
先说文件导入,文件来源分为几个方向:本地上传,OSS下载。在实现时要支持各种情况的导入。
再说文件读取,文件读取指将Excel的数据读到内存中,数据结构和excel本身保持一致。
再说一下文件解析,主要是将读取的excel数据结构转为数据库的结构对象。回想一下,我们导入的Excel每个列主要有以下几种情况:

  • 判重:唯一键列(code)不能重复,包括excel内不能重复,并且和数据库不能重复。
  • 判存在:对外键的引用要存在,如果是外链编码有时需要转为外链id。
  • 类型转换:枚举的中文要转英文;启用中文转为true;一堆用逗号分隔的类型转为List。

这三种需求基本满足了90%的文件导入需求,所以我们的设计最后要完整覆盖这三种需求,减少新增导入时的代码开发量。
最后我们聊聊将转换好的数据结构对象插入数据库。此时根据唯一键可以实现,只新增或者重复时更新两种方案。关于SQL的编写可以查看我的一篇文章:sql解析

三、技术选型

文件导入

文件导入主要是本地文件上传(直接输入InputStream流)和 由用户上传网络后,程序自动下载。本地上传不用说依赖各自前端系统框架,框架不同实现方法不同。对于网络下载,我们采用阿里云OSS作为网络存储媒介,官方给开发提供了完备的SDK。

文件读取

Java领域解析、生成Excel比较有名的框架有Apache poi、jxl等。但他们都存在一个严重的问题就是非常的耗内存。如果你的系统并发量不大的话可能还行,但是一旦并发上来后一定会OOM或者JVM频繁的full gc。
easyExcel是阿里巴巴开源的一个excel处理框架,以使用简单、节省内存著称。64M内存1分钟内读取75M(46W行25列)的Excel。让我们彻底拒绝OOM!官方入门手册参考


四、程序设计

整体设计



我们将新增三个接口和一个模板方法类用于Excel导入:

  • ExcelData
    • ExcelData 对象的列 和Excel 文件 的列一一对应
  • ExcelDomain
    • Domain是DDD(领域驱动设计)的概念,之所以用个是因为我们系统是DDD架构的。没用DDD的可以对应大家熟悉的DTO。文件解析流程就是将EasyExcel读取的ExcelData转为我们自己的数据结构ExcelDomain。
  • ExcelRepository_<T extends ExcelDomain>_
    • ExcelRepository 给插入数据库流程提供了将ExcelDomain插入数据库的流程
  • ExcelTemplate_<T extends ExcelData>_
    • ExcelTemplate 是模板方法,在三个流程中起串联作用,主要提供文件下载,文件读取和通用的文件解析方法。


整体流程: ExcelTemplate从OSS下载或本地导入Excel文件,使用easyExcel读取文件到内存中,将读取好的数据list交给各个具体实现类。各个实现类使用ExcelTemplate按照自有逻辑解析成数据库对象,最后调用ExcelRepository将数据插入数据库。

详细设计

详细设计

  1. ExcelData

实现类的ExcelData 需要自己实现接口,用于文件读取流程。一般是根据唯一主键是否存在,当作excel里面数据行是否有效的依据。

public interface ExcelData {
   

    /**
     * 检验读取到的Excel行是否有效
     * @return  本行数据是否有效
     */
    boolean isValid();
}


2. ExcelDomain
这个接口只是为了为了实现文件解析流程将外链编码转为外链Id功能对DTO做的约束。

public interface ExcelDomain {
   
    void setId(Long id);
    Long getId();

    void setCode(String code);
    String getCode();
}
  1. ExcelRepository

插入数据库流程,主要依赖这个接口实现。

public interface ExcelRepository<T extends ExcelDomain> {
   
    /**
     * 通过编码批量查询
     *
     * @param codeList 编码
     * @param tableKey 可空的分库分表键
     * @return 符合 的对象列表
     */
    List<T> listByCodeList(@NonNull List<String> codeList, @Nullable Long tableKey);

    /**
     * 批量插入
     *
     * @param list
     */
    void batchSave(List<T> list);
}
  1. ExcelTemplate

    这个类是模板方法类。这里引入了一个自己封装的OssTool,主要负责跟阿里云Oss交互,定义这个类可以屏蔽阿里云自己内部细节,实现面向接口编程。ImportDataDistributedLock 这个类是自己实现的一个分布式锁类,主要用于保证:一个文件只能同时有一个人导入数据。ImportDataDistributedLock 的具体实现可以参考:分布式锁的方法论
  • importExcelData
    • 主要实现 文件导入
  • transferExcelData
    • 主要实现 文件读取
    • 在文件读取流程中,如何知道自类要转为什么结构的ExcelData,这里我们使用了泛型。但是**运行时如何获得实际类的class传给EasyExcel解析**呢? 这是本文的难点,这里的解决方案和gson 及 fastjson 在处理泛型时用到的原理一致。
  • processExcelList
    • 主要定义 文件解析 接口
  • processAndOutLinkCheck、processCodeCheck、processEnable
    • 这三个都是给用户用的辅助文件解析的方法。
@Slf4j(topic = "APPLICATION")
public abstract class ExcelTemplate<T extends ExcelData> {
   
    @Resource
    private OssTool ossTool;

    @Resource
    private ImportDataDistributedLock distributedLock;

    /**
     * 导入文件
     * 导入Excel数据,导入优先级为:
     * inputStream > dirAndFile > stepEnum 既 :
     * 本地文件流 》 网络下载地址 》 默认下载地址
     *
     * @param operatorId  操作人
     * @param stepEnum    InitializeStepEnum
     * @param inputStream 文件流
     * @param dirAndFile  文件下载路径《dir,file》
     * @param updateFlag  是初始化数据还是批量更新数据
     * @return 文件导入结果
     */
    public Result<Boolean> importExcelData(@NonNull Long operatorId, @NonNull InitializeStepEnum stepEnum,
                                           @Nullable InputStream inputSt
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值