• easyExcel的导入


    1、基本的导入方法
    1. /**
    2. * 从Excel导入会员列表
    3. */
    4. @RequestMapping(value = "/import1", method = RequestMethod.POST)
    5. @ResponseBody
    6. public void importMemberList(@RequestPart("file") MultipartFile file) throws IOException {
    7. List<Member> list = EasyExcel.read(file.getInputStream())
    8. .head(Member.class)
    9. .sheet()
    10. .doReadSync();
    11. for (Member member : list) {
    12. System.out.println(member);
    13. }
    14. }

    注意,在上述代码中,最终调用的是doReadSync()方法。

    2、模拟500w数据导入

    1、首先是分批读取读取Excel中的500w数据,这一点EasyExcel有自己的解决方案,我们可以参考Demo即可,只需要把它分批的参数5000调大即可。

    2、其次就是往DB里插入,怎么去插入这20w条数据,当然不能一条一条的循环,应该批量插入这20w条数据,同样也不能使用Mybatis的批量插入语,因为效率也低。

    3、使用JDBC+事务的批量操作将数据插入到数据库。(分批读取+JDBC分批插入+手动事务控制)

    代码实现

    controller层测试接口

    1. @Resource
    2. private EmpService empService;
    3. @GetMapping("/importData")
    4. public void importData() {
    5. String fileName = "C:\\Users\\asus\\Desktop\\员工信息.xlsx";
    6. //记录开始读取Excel时间,也是导入程序开始时间
    7. long startReadTime = System.currentTimeMillis();
    8. System.out.println("------开始读取Excel的Sheet时间(包括导入数据过程):" + startReadTime + "ms------");
    9. //读取所有Sheet的数据.每次读完一个Sheet就会调用这个方法
    10. EasyExcel.read(fileName, new EasyExceGeneralDatalListener(empService)).doReadAll();
    11. long endReadTime = System.currentTimeMillis();
    12. System.out.println("------结束读取Excel的Sheet时间(包括导入数据过程):" + endReadTime + "ms------");
    13. System.out.println("------读取Excel的Sheet时间(包括导入数据)共计耗时:" + (endReadTime-startReadTime) + "ms------");
    14. }

    Excel导入事件监听

    1. // 事件监听
    2. public class EasyExceGeneralDatalListener extends AnalysisEventListener<Map<Integer, String>> {
    3. /**
    4. * 处理业务逻辑的Service,也可以是Mapper
    5. */
    6. private EmpService empService;
    7. /**
    8. * 用于存储读取的数据
    9. */
    10. private List<Map<Integer, String>> dataList = new ArrayList>();
    11. public EasyExceGeneralDatalListener() {
    12. }
    13. public EasyExceGeneralDatalListener(EmpService empService) {
    14. this.empService = empService;
    15. }
    16. @Override
    17. public void invoke(Map<Integer, String> data, AnalysisContext context) {
    18. //数据add进入集合
    19. dataList.add(data);
    20. //size是否为100000条:这里其实就是分批.当数据等于10w的时候执行一次插入
    21. if (dataList.size() >= ExcelConstants.GENERAL_ONCE_SAVE_TO_DB_ROWS) {
    22. //存入数据库:数据小于1w条使用Mybatis的批量插入即可;
    23. saveData();
    24. //清理集合便于GC回收
    25. dataList.clear();
    26. }
    27. }
    28. /**
    29. * 保存数据到DB
    30. *
    31. * @param
    32. * @MethodName: saveData
    33. * @return: void
    34. */
    35. private void saveData() {
    36. empService.importData(dataList);
    37. dataList.clear();
    38. }
    39. /**
    40. * Excel中所有数据解析完毕会调用此方法
    41. *
    42. * @param: context
    43. * @MethodName: doAfterAllAnalysed
    44. * @return: void
    45. */
    46. @Override
    47. public void doAfterAllAnalysed(AnalysisContext context) {
    48. saveData();
    49. dataList.clear();
    50. }
    51. }

    核心测试代码:

    1. /*
    2. * 测试用Excel导入超过10w条数据,经过测试发现,使用Mybatis的批量插入速度非常慢,所以这里可以使用 数据分批+JDBC分批插入+事务来继续插入速度会非常快
    3. */
    4. @Override
    5. public void importData(List<Map<Integer, String>> dataList) {
    6. //结果集中数据为0时,结束方法.进行下一次调用
    7. if (dataList.size() == 0) {
    8. return;
    9. }
    10. //JDBC分批插入+事务操作完成对20w数据的插入
    11. Connection conn = null;
    12. PreparedStatement ps = null;
    13. try {
    14. long startTime = System.currentTimeMillis();
    15. System.out.println(dataList.size() + "条,开始导入到数据库时间:" + startTime + "ms");
    16. conn = JDBCDruidUtils.getConnection();
    17. //控制事务:默认不提交
    18. conn.setAutoCommit(false);
    19. String sql = "insert into emp (`empno`, `ename`, `job`, `mgr`, `hiredate`, `sal`, `comm`, `deptno`) values";
    20. sql += "(?,?,?,?,?,?,?,?)";
    21. ps = conn.prepareStatement(sql);
    22. //循环结果集:这里循环不支持lambda表达式
    23. for (int i = 0; i < dataList.size(); i++) {
    24. Map<Integer, String> item = dataList.get(i);
    25. ps.setString(1, item.get(0));
    26. ps.setString(2, item.get(1));
    27. ps.setString(3, item.get(2));
    28. ps.setString(4, item.get(3));
    29. ps.setString(5, item.get(4));
    30. ps.setString(6, item.get(5));
    31. ps.setString(7, item.get(6));
    32. ps.setString(8, item.get(7));
    33. //将一组参数添加到此 PreparedStatement 对象的批处理命令中。
    34. ps.addBatch();
    35. }
    36. //执行批处理
    37. ps.executeBatch();
    38. //手动提交事务
    39. conn.commit();
    40. long endTime = System.currentTimeMillis();
    41. System.out.println(dataList.size() + "条,结束导入到数据库时间:" + endTime + "ms");
    42. System.out.println(dataList.size() + "条,导入用时:" + (endTime - startTime) + "ms");
    43. } catch (Exception e) {
    44. e.printStackTrace();
    45. } finally {
    46. //关连接
    47. JDBCDruidUtils.close(conn, ps);
    48. }
    49. }
    50. }

    JDBC工具类

    1. //JDBC工具类
    2. public class JDBCDruidUtils {
    3. private static DataSource dataSource;
    4. /*
    5. 创建数据Properties集合对象加载加载配置文件
    6. */
    7. static {
    8. Properties pro = new Properties();
    9. //加载数据库连接池对象
    10. try {
    11. //获取数据库连接池对象
    12. pro.load(JDBCDruidUtils.class.getClassLoader().getResourceAsStream("druid.properties"));
    13. dataSource = DruidDataSourceFactory.createDataSource(pro);
    14. } catch (Exception e) {
    15. e.printStackTrace();
    16. }
    17. }
    18. /*
    19. 获取连接
    20. */
    21. public static Connection getConnection() throws SQLException {
    22. return dataSource.getConnection();
    23. }
    24. /**
    25. * 关闭conn,和 statement独对象资源
    26. *
    27. * @param connection
    28. * @param statement
    29. * @MethodName: close
    30. * @return: void
    31. */
    32. public static void close(Connection connection, Statement statement) {
    33. if (connection != null) {
    34. try {
    35. connection.close();
    36. } catch (SQLException e) {
    37. e.printStackTrace();
    38. }
    39. }
    40. if (statement != null) {
    41. try {
    42. statement.close();
    43. } catch (SQLException e) {
    44. e.printStackTrace();
    45. }
    46. }
    47. }
    48. /**
    49. * 关闭 conn , statement 和resultset三个对象资源
    50. *
    51. * @param connection
    52. * @param statement
    53. * @param resultSet
    54. * @MethodName: close
    55. * @return: void
    56. */
    57. public static void close(Connection connection, Statement statement, ResultSet resultSet) {
    58. close(connection, statement);
    59. if (resultSet != null) {
    60. try {
    61. resultSet.close();
    62. } catch (SQLException e) {
    63. e.printStackTrace();
    64. }
    65. }
    66. }
    67. /*
    68. 获取连接池对象
    69. */
    70. public static DataSource getDataSource() {
    71. return dataSource;
    72. }
    73. }

    druid.properties配置文件

    1. # druid.properties配置
    2. driverClassName=com.mysql.jdbc.Driver
    3. url=jdbc:mysql://localhost:3306/llp?autoReconnect=true&useUnicode=true&useSSL=false&serverTimezone=GMT%2B8&allowMultiQueries=true&rewriteBatchedStatements=true
    4. username=root
    5. password=root
    6. initialSize=10
    7. maxActive=50
    8. maxWait=60000

    这里我将文件创建在类路径下,需要注意的是连接mysql数据库时需要指定rewriteBatchedStatements=true批处理才会生效,否则还是逐条插入效率较低,allowMultiQueries=true表示可以使sql语句中有多个insert或者update语句(语句之间携带分号),这里可以忽略。

    总结

    1.如此大批量数据的导出和导入操作,会占用大量的内存实际开发中还应限制操作人数。

    2.在做大批量的数据导入时,可以使用jdbc手动开启事务,批量提交。

  • 相关阅读:
    QML 怎么调用 C++ 中的内容?
    Topic太多,RocketMQ炸了!
    系统平台:新店如何打造爆款
    【JAVA项目实战】【图书管理系统】用户添加功能【Servlet】+【Jsp】+【Mysql】
    大厂FPGA的面试题
    Zabbix技术分享——如何使用zabbix监控华为云RDS
    【电驱动】驱动电机系统讲解
    YOLOV7详细解读(三)技术要点归纳
    TNF6TOA TNF5TOA 全新板卡8路任意速率业务支路处理板
    QT连接OpenCV库实现人脸识别
  • 原文地址:https://blog.csdn.net/u011518709/article/details/133760977