精华内容
下载资源
问答
  • 多线程导入excel 数据

    2018-01-04 21:54:05
    java 多线程导入excel数据,预防高并发,线程同步锁,
  • 你只要用缓存,就可能会涉及到缓存与数据库双存储双写,你只要是双写,就一定会有数据一致性的问题,那么你如何解决一致性问题? 面试题剖析 一般来说,如果允许缓存可以稍微的跟数据库偶尔有不一致的情况,也就是说...

    面试题

    如何保证缓存与数据库的双写一致性?

    面试官心理分析

    你只要用缓存,就可能会涉及到缓存与数据库双存储双写,你只要是双写,就一定会有数据一致性的问题,那么你如何解决一致性问题?

    面试题剖析

    一般来说,如果允许缓存可以稍微的跟数据库偶尔有不一致的情况,也就是说如果你的系统不是严格要求 “缓存+数据库” 必须保持一致性的话,最好不要做这个方案,即:读请求和写请求串行化,串到一个内存队列里去。

    串行化可以保证一定不会出现不一致的情况,但是它也会导致系统的吞吐量大幅度降低,用比正常情况下多几倍的机器去支撑线上的一个请求。

    Cache Aside Pattern

    最经典的缓存+数据库读写的模式,就是 Cache Aside Pattern。

    • 读的时候,先读缓存,缓存没有的话,就读数据库,然后取出数据后放入缓存,同时返回响应。
    • 更新的时候,先更新数据库,然后再删除缓存

    为什么是删除缓存,而不是更新缓存?

    原因很简单,很多时候,在复杂点的缓存场景,缓存不单单是数据库中直接取出来的值。

    比如可能更新了某个表的一个字段,然后其对应的缓存,是需要查询另外两个表的数据并进行运算,才能计算出缓存最新的值的。

    另外更新缓存的代价有时候是很高的。是不是说,每次修改数据库的时候,都一定要将其对应的缓存更新一份?也许有的场景是这样,但是对于比较复杂的缓存数据计算的场景,就不是这样了。如果你频繁修改一个缓存涉及的多个表,缓存也频繁更新。但是问题在于,这个缓存到底会不会被频繁访问到?

    举个栗子,一个缓存涉及的表的字段,在 1 分钟内就修改了 20 次,或者是 100 次,那么缓存更新 20 次、100 次;但是这个缓存在 1 分钟内只被读取了 1 次,有大量的冷数据。实际上,如果你只是删除缓存的话,那么在 1 分钟内,这个缓存不过就重新计算一次而已,开销大幅度降低。用到缓存才去算缓存。

    其实删除缓存,而不是更新缓存,就是一个 lazy 计算的思想,不要每次都重新做复杂的计算,不管它会不会用到,而是让它到需要被使用的时候再重新计算。像 mybatis,hibernate,都有懒加载思想。查询一个部门,部门带了一个员工的 list,没有必要说每次查询部门,都把里面的 1000 个员工的数据也同时查出来啊。80% 的情况,查这个部门,就只是要访问这个部门的信息就可以了。先查部门,同时要访问里面的员工,那么这个时候只有在你要访问里面的员工的时候,才会去数据库里面查询 1000 个员工。

    最初级的缓存不一致问题及解决方案

    问题:先更新数据库,再删除缓存。如果删除缓存失败了,那么会导致数据库中是新数据,缓存中是旧数据,数据就出现了不一致。

    解决思路:先删除缓存,再更新数据库。如果数据库更新失败了,那么数据库中是旧数据,缓存中是空的,那么数据不会不一致。因为读的时候缓存没有,所以去读了数据库中的旧数据,然后更新到缓存中。

    比较复杂的数据不一致问题分析

    数据发生了变更,先删除了缓存,然后要去修改数据库,此时还没修改。一个请求过来,去读缓存,发现缓存空了,去查询数据库,查到了修改前的旧数据,放到了缓存中。随后数据变更的程序完成了数据库的修改。完了,数据库和缓存中的数据不一样了…

    为什么上亿流量高并发场景下,缓存会出现这个问题?

    只有在对一个数据在并发的进行读写的时候,才可能会出现这种问题。其实如果说你的并发量很低的话,特别是读并发很低,每天访问量就 1 万次,那么很少的情况下,会出现刚才描述的那种不一致的场景。但是问题是,如果每天的是上亿的流量,每秒并发读是几万,每秒只要有数据更新的请求,就可能会出现上述的数据库+缓存不一致的情况

    解决方案如下:

    更新数据的时候,根据数据的唯一标识,将操作路由之后,发送到一个 jvm 内部队列中。读取数据的时候,如果发现数据不在缓存中,那么将重新读取数据+更新缓存的操作,根据唯一标识路由之后,也发送同一个 jvm 内部队列中。

    一个队列对应一个工作线程,每个工作线程串行拿到对应的操作,然后一条一条的执行。这样的话,一个数据变更的操作,先删除缓存,然后再去更新数据库,但是还没完成更新。此时如果一个读请求过来,没有读到缓存,那么可以先将缓存更新的请求发送到队列中,此时会在队列中积压,然后同步等待缓存更新完成。

    这里有一个优化点,一个队列中,其实多个更新缓存请求串在一起是没意义的,因此可以做过滤,如果发现队列中已经有一个更新缓存的请求了,那么就不用再放个更新请求操作进去了,直接等待前面的更新操作请求完成即可。

    待那个队列对应的工作线程完成了上一个操作的数据库的修改之后,才会去执行下一个操作,也就是缓存更新的操作,此时会从数据库中读取最新的值,然后写入缓存中。

    如果请求还在等待时间范围内,不断轮询发现可以取到值了,那么就直接返回;如果请求等待的时间超过一定时长,那么这一次直接从数据库中读取当前的旧值。

    高并发的场景下,该解决方案要注意的问题:

    • 读请求长时阻塞

    由于读请求进行了非常轻度的异步化,所以一定要注意读超时的问题,每个读请求必须在超时时间范围内返回。

    该解决方案,最大的风险点在于说,可能数据更新很频繁,导致队列中积压了大量更新操作在里面,然后读请求会发生大量的超时,最后导致大量的请求直接走数据库。务必通过一些模拟真实的测试,看看更新数据的频率是怎样的。

    另外一点,因为一个队列中,可能会积压针对多个数据项的更新操作,因此需要根据自己的业务情况进行测试,可能需要部署多个服务,每个服务分摊一些数据的更新操作。如果一个内存队列里居然会挤压 100 个商品的库存修改操作,每个库存修改操作要耗费 10ms 去完成,那么最后一个商品的读请求,可能等待 10 * 100 = 1000ms = 1s 后,才能得到数据,这个时候就导致读请求的长时阻塞

    一定要做根据实际业务系统的运行情况,去进行一些压力测试,和模拟线上环境,去看看最繁忙的时候,内存队列可能会挤压多少更新操作,可能会导致最后一个更新操作对应的读请求,会 hang 多少时间,如果读请求在 200ms 返回,如果你计算过后,哪怕是最繁忙的时候,积压 10 个更新操作,最多等待 200ms,那还可以的。

    如果一个内存队列中可能积压的更新操作特别多,那么你就要加机器,让每个机器上部署的服务实例处理更少的数据,那么每个内存队列中积压的更新操作就会越少。

    其实根据之前的项目经验,一般来说,数据的写频率是很低的,因此实际上正常来说,在队列中积压的更新操作应该是很少的。像这种针对读高并发、读缓存架构的项目,一般来说写请求是非常少的,每秒的 QPS 能到几百就不错了。

    我们来实际粗略测算一下

    如果一秒有 500 的写操作,如果分成 5 个时间片,每 200ms 就 100 个写操作,放到 20 个内存队列中,每个内存队列,可能就积压 5 个写操作。每个写操作性能测试后,一般是在 20ms 左右就完成,那么针对每个内存队列的数据的读请求,也就最多 hang 一会儿,200ms 以内肯定能返回了。

    经过刚才简单的测算,我们知道,单机支撑的写 QPS 在几百是没问题的,如果写 QPS 扩大了 10 倍,那么就扩容机器,扩容 10 倍的机器,每个机器 20 个队列。

    • 读请求并发量过高

    这里还必须做好压力测试,确保恰巧碰上上述情况的时候,还有一个风险,就是突然间大量读请求会在几十毫秒的延时 hang 在服务上,看服务能不能扛的住,需要多少机器才能扛住最大的极限情况的峰值。

    但是因为并不是所有的数据都在同一时间更新,缓存也不会同一时间失效,所以每次可能也就是少数数据的缓存失效了,然后那些数据对应的读请求过来,并发量应该也不会特别大。

    • 多服务实例部署的请求路由

    可能这个服务部署了多个实例,那么必须保证说,执行数据更新操作,以及执行缓存更新操作的请求,都通过 Nginx 服务器路由到相同的服务实例上

    比如说,对同一个商品的读写请求,全部路由到同一台机器上。可以自己去做服务间的按照某个请求参数的 hash 路由,也可以用 Nginx 的 hash 路由功能等等。

    • 热点商品的路由问题,导致请求的倾斜

    万一某个商品的读写请求特别高,全部打到相同的机器的相同的队列里面去了,可能会造成某台机器的压力过大。就是说,因为只有在商品数据更新的时候才会清空缓存,然后才会导致读写并发,所以其实要根据业务系统去看,如果更新频率不是太高的话,这个问题的影响并不是特别大,但是的确可能某些机器的负载会高一些。

    最后

    面试是跳槽涨薪最直接有效的方式,马上金九银十来了,各位做好面试造飞机,工作拧螺丝的准备了吗?

    掌握了这些知识点,面试时在候选人中又可以夺目不少,暴击9999点。机会都是留给有准备的人,只有充足的准备,才可能让自己可以在候选人中脱颖而出。

    如果你需要这份完整版的面试笔记,只需你多多支持我这篇文章。

    ——对文章进行点赞+评论,关注我,然后再点击这里免费领取

    ——对文章进行点赞+评论,关注我,然后再点击这里免费领取**

    [外链图片转存中…(img-kUX7b3QH-1625842610312)]

    展开全文
  • 最近项目写了poi导入excel数据到数据库,在代码上已经算是很优了,虽然领导没有要求我优化导入接口,但是本着技术而言,想把学到的知识用于实践,于是使用多线程方式导入excel。 所需pow依赖: <dependency> ...

    需求:
    最近项目写了poi导入excel数据到数据库,在代码上已经算是很优了,虽然领导没有要求我优化导入接口,但是本着技术而言,想把学到的知识用于实践,于是使用多线程方式导入excel。

    所需pow依赖:

    		<dependency>
    			<groupId>org.apache.poi</groupId>
    			<artifactId>poi</artifactId>
    			<version>3.17</version>
    		</dependency>
    		<dependency>
    			<groupId>org.apache.poi</groupId>
    			<artifactId>poi-ooxml</artifactId>
    			<version>3.17</version>
    		</dependency>
    

    导入的service实现类:

    /**
         * 多线程导入
         * @param file
         * @return
         * @throws Exception
         */
        @Override
        public Map<String,Object> importData(MultipartFile file) throws Exception{
            final Date now = new Date();
            SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            logger.info("{},开始导入数据...", format.format(now));
            //设置一个信号量为5的信号量,限制同时运行的线程数量最大为5
            Semaphore semaphore = new Semaphore(10);
    
            Map<String,Object> map = new HashMap<>();
            //多线程编程需要一个线程安全的ArrayList
            List<ErrorInfo> list = Collections.synchronizedList(new ArrayList<ErrorInfo>());
            Workbook workbook = null;
            String filename = file.getOriginalFilename();
            if(filename.endsWith("xls")){
                workbook = new HSSFWorkbook(file.getInputStream());
            }else if(filename.endsWith("xlsx")){
                workbook = new XSSFWorkbook(file.getInputStream());
            }else {
                ErrorInfo errorInfo = new ErrorInfo();
                errorInfo.setErrorMsg("请上传xlx或xlsx格式的文件");
                list.add(errorInfo);
                map.put("code",501);
                map.put("data",list);
                return map;
            }
            Sheet sheet = workbook.getSheetAt(0);
            int physicalNumberOfRows = sheet.getPhysicalNumberOfRows();
            logger.info("获取到workbook中的总行数:{}" ,physicalNumberOfRows);
            //第一行是表头,实际行数要减1
            int rows = physicalNumberOfRows - 1;
            //一个线程让他处理200个row,也许可以处理更多吧
            int threadNum = rows/200 + 1; //线程数量
    
            //设置一个倒计时门闩,用来处理主线程等待蚂蚁线程执行完成工作之后再运行
            CountDownLatch countDownLatch = new CountDownLatch(threadNum);
            //查询是否重名
            Set<String> names = this.findAllUser().stream().map(User::getUsername).collect(Collectors.toSet());
            //创建一个定长的线程池
            ExecutorService executorService = Executors.newFixedThreadPool(threadNum);
    
            logger.info("开始创建线程,数据总行数:{},线程数量:{}",rows,threadNum);
    
            List<Future<Integer>> futures = new ArrayList<>();
            int successCount = 0;
    
            for(int i = 1; i <= threadNum; i++){
    
                int startRow = (i-1)*200 +1;
                int endRow = i*200;
                if(i == threadNum){
                    endRow = rows;
                }
                logger.info("开始执行线程方法,线程ID:<{}>,线程名称:<{}>",Thread.currentThread().getId(),Thread.currentThread().getName());
                Future<Integer> future = executorService.submit(new UserThread(semaphore,workbook, startRow, endRow, list, names,this,countDownLatch));
                futures.add(future);
                logger.info("结束线程执行方法,返回结果:<{}>,当前线程ID:<{}>,当前线程名称:<{}>",JSON.toJSONString(future),Thread.currentThread().getId(),Thread.currentThread().getName());
                //get方法中可以设置超时时间,即规定时间内没有返回结果,则继续运行
                //get方法是线程阻塞的,调用get方法会导致后续线程因主线程阻塞而没有创建,达不到效果。
                //successCount += future.get();
            }
            //主线程等待子线程完成任务,60秒还没执行完成就继续执行
    
            for(Future<Integer> future : futures){
                    successCount += future.get();
            }
    		//主线程等待子线程全部跑完才继续运行。设置60秒等待时间,超时后继续执行。
            countDownLatch.await(60,TimeUnit.SECONDS);
            executorService.shutdown();
    
            Date endDate = new Date();
            long difference = endDate.getTime() - now.getTime();
            String duration = DurationFormatUtils.formatDuration(difference, "HH:mm:ss");
            logger.info("执行完成,错误信息:{}", JSON.toJSONString(list));
            logger.info("{},结束导入,共{}条数据,导入成功:{},耗时={}", format.format(endDate), rows,successCount,duration);
            map.put("code",200);
            map.put("msg","结束导入,共" + rows + "条数据,导入成功" + successCount + "条,耗时:" +duration);
            map.put("data",list);
            return map;
        }
    

    导入线程类:

    package com.thread.demo.thread;
    
    import com.thread.demo.common.ErrorInfo;
    import com.thread.demo.entity.User;
    import com.thread.demo.service.UserService;
    import org.apache.poi.ss.usermodel.Cell;
    import org.apache.poi.ss.usermodel.Row;
    import org.apache.poi.ss.usermodel.Sheet;
    import org.apache.poi.ss.usermodel.Workbook;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    
    import java.util.List;
    import java.util.Set;
    import java.util.concurrent.Callable;
    import java.util.concurrent.CountDownLatch;
    import java.util.concurrent.Semaphore;
    
    /**
     * @Author Honey
     * @Date 2019/11/15 10:31
     * @Description
     **/
    public class UserThread implements Callable<Integer>{
    
        private Logger logger = LoggerFactory.getLogger(UserThread.class);
    
        private Workbook workbook;
    
        private Integer startRow;
    
        private Integer endRow;
    
        private List<ErrorInfo> errorInfoList;
    
        private Set<String> names;
    
        private UserService userService;
    
        private Semaphore semaphore;
    
        private CountDownLatch latch;
    
        public UserThread(Semaphore semaphore,Workbook workbook,Integer startRow,Integer endRow,List<ErrorInfo> errorInfoList,Set<String> names,UserService userService,CountDownLatch latch){
            this.workbook = workbook;
            this.startRow = startRow;
            this.endRow = endRow;
            this.errorInfoList = errorInfoList;
            this.names = names;
            this.userService = userService;
            this.semaphore = semaphore;
            this.latch = latch;
        }
    
        @Override
        public Integer call() throws Exception {
            logger.info("线程ID:<{}>开始运行,startRow:{},endRow:{}",Thread.currentThread().getId(),startRow,endRow);
            semaphore.acquire();
            logger.info("消耗了一个信号量,剩余信号量为:{}",semaphore.availablePermits());
            latch.countDown();
            Sheet sheet = workbook.getSheetAt(0);
            int count = 0;
            for(int i = startRow; i <= endRow; i++){
                User user = new User();
                Row row = sheet.getRow(i);
                Cell cell1 = row.getCell(0);
                String username = cell1.getStringCellValue();
                user.setUsername(username);
                user.setPassword("123456");
                Cell cell2 = row.getCell(1);
                String realname = cell2.getStringCellValue();
                user.setRealName(realname);
                if(names.contains(username)){
                    ErrorInfo errorInfo = new ErrorInfo();
                    errorInfo.setRow(startRow);
                    errorInfo.setColumn(1);
                    errorInfo.setErrorMsg("第" + startRow + "行用户账号已存在");
                    errorInfoList.add(errorInfo);
                    break;
                }
                count += userService.addUser(user);
            }
            semaphore.release();
            return count;
        }
    }
    
    

    controller也贴一下吧。没什么东西

    package com.thread.demo.controller;
    
    import com.thread.demo.service.UserService;
    import org.apache.poi.ss.usermodel.Workbook;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.PostMapping;
    import org.springframework.web.bind.annotation.RestController;
    import org.springframework.web.multipart.MultipartFile;
    
    import javax.servlet.http.HttpServletResponse;
    import java.util.Map;
    
    /**
     * @Author Honey
     * @Date 2019/11/15 10:27
     * @Description
     **/
    @RestController
    public class UserController {
    
        @Autowired
        private UserService userService;
    
        /**
         * 多线程导入
         * @param file
         * @return
         */
        @PostMapping("/importManyThread")
        public Map importData(MultipartFile file){
            Map<String, Object> map = null;
            try {
                map = userService.importData(file);
                return map;
            } catch (Exception e) {
                e.printStackTrace();
                map.put("code",501);
                map.put("msg","数据出错");
                return map;
            }
        }
    
        /**
         * 单线程导入
         * @param file
         * @return
         */
        @PostMapping("/importSingleThread")
        public Map importData2(MultipartFile file){
            Map<String, Object> map = null;
            try {
                map = userService.importDataYiBan(file);
                return map;
            } catch (Exception e) {
                e.printStackTrace();
                map.put("code",501);
                map.put("msg","数据出错");
                return map;
            }
        }
    
        /**
         * 导出excel
         * @param response
         * @throws Exception
         */
        @GetMapping("/export")
        public void exportData(HttpServletResponse response) throws Exception{
            Workbook workbook = userService.exportData();
            response.setContentType("application/vnd.ms-excel;charset=utf-8");
            response.setCharacterEncoding("UTF-8");
            //test.xls是弹出下载对话框的文件名,不能为中文,中文请自行编码
            response.setHeader("Content-Disposition", "attachment;filename=user.xlsx");
            workbook.write(response.getOutputStream());
        }
    
    }
    
    

    执行结果:
    在这里插入图片描述

    使用多线程方式导入5000条数据花费时间14秒,而单线程导入则需1分钟14秒。
    可见多线程方式运行程序是可以达到空间换时间的目的的。

    展开全文
  • java excel导入多线程批量插入数据库

    万次阅读 多人点赞 2018-06-14 17:02:29
    最近写了个excel导入并多线程持久化到数据库的功能,捣鼓了一天才弄好,先记录下来防止自己忘了。 (1)先controller类中方法。 @AccessLog @ApiOperation(value = "导入excel", httpMethod = "...

    最近写了个excel导入并多线程持久化到数据库的功能,捣鼓了一天才弄好,先记录下来防止自己忘了。

    (1)先controller类中方法。

    @AccessLog
        @ApiOperation(value = "导入excel", httpMethod = "POST", notes = "导入excel")
        @RequestMapping(value = "/importExcel",method = RequestMethod.POST)
        @ApiImplicitParams({
                @ApiImplicitParam(name="postionId",value="岗位ID",dataType="long", paramType = "query"),
                @ApiImplicitParam(name="typeId",value="类型ID(1:岗位 2:人员)",dataType="int", paramType = "query"),
                @ApiImplicitParam(name="agencyId",value="部门ID",dataType="long", paramType = "query")
        })
        public ResponseResult importExcel(@RequestParam(value="file") MultipartFile file,
                                            Long postionId, Integer typeId, Long agencyId) {
            SelAgencyAndPostionVO selAgencyAndPostionVO = new SelAgencyAndPostionVO(agencyId,postionId,typeId);
            if (null == selAgencyAndPostionVO) {
                return new ResponseResult(ExceptionCode.PARAM_IS_NULL);
            }
            //类型标识(1:岗位 2:人员)
            typeId = selAgencyAndPostionVO.getTypeId();
            if (null == typeId) {
                log.info("1", "typeId is null");
                return new ResponseResult(ExceptionCode.PARAM_IS_NULL);
            }
            //获取上传的文件名称;
            String name = file.getOriginalFilename();
            //判断是否为excel类型文件
            if(!name.endsWith(".xls") && !name.endsWith(".xlsx")){
                log.info("导入的文件不是excel类型");
                return  new ResponseResult<>("导入的文件不是excel类型");
            }
    
            try {
                HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.
                        getRequestAttributes()).getRequest();
    
                //上传至绝对路径
                String path = request.getSession().getServletContext().getRealPath(File.separator);
                String uploadDir = path+"upload"+File.separator;
                log.info(this.getClass().getName()+"临时保存图片路径saveImgUrl:"+uploadDir);
    
                File f = new File(uploadDir);
                //如果不存在该路径就创建
                if (!f.exists()) {
                    f.mkdir();
                }
                //获取文件名
                String uuid= new Date().getTime()+"_"+UUID.randomUUID().toString().
                        replace("-","").substring(0,6);
    
                //文件保存绝对路径
                String newName = uploadDir+ uuid + "_"+name;
    
                //上传文件位置
                File dir = new File(uploadDir);
                if (!dir.exists()) {
                    dir.mkdirs();
                }
                File imgFile = new File(newName);
                //存入临时内存
                FileUtils.writeByteArrayToFile(imgFile, file.getBytes());
    
                //获取excel中的数据信息
                List<Map<String, Object>> maps = ImportExcelFileUtil.getDataFromExcel(newName,typeId == 1 ? new ElPositionDTO() :
                                                                                        typeId == 2 ? new ElUserInfoDTO(): null);
                //删除临时保存的图片
                if(imgFile.exists() && imgFile.isFile()) {
                    imgFile.delete();
                }
    
                if (CollectionUtils.isEmpty(maps)) {
                    log.error("ElAttachmentController的importExcel方法获取导入的excel数据为空");
                    return  new ResponseResult<>(ExceptionCode.METHOD_FAILURE);
                }
    
                //获取的是成功插入的次数
                int row = elAgencyPositionUserService.importBatchData(maps,selAgencyAndPostionVO);
                String result = "";
                if ((maps.size() - row) == 0  ) {
                    result = "全部导入成功"+row+"条";
                } else if ((maps.size() - row) > 0) {
                    result ="导入成功"+row+"条,导入失败" + (maps.size() - row) + "条(错误或重复)";
                }
                return new ResponseResult(result);
            }catch(BusinessException e){
                log.error("ElAttachmentController的importExcel方法error"+e.getMessage(),e);
                return new ResponseResult<>(e);
            }catch (Exception e) {
                log.error("ElAttachmentController的importExcel异常"+e.getMessage(), e);
                return new ResponseResult(ExceptionCode.INTERFACE_USE_FAILURE);
            }
        }

    (2)InportExcelFileUtil类处理excel文件的信息。此excel方法是通用的方法

    package com.xxx.utils;
    
    import com.xxx.dto.ElUserInfoDTO;
    import org.apache.commons.lang3.reflect.FieldUtils;
    import org.apache.poi.hssf.usermodel.HSSFCell;
    import org.apache.poi.hssf.usermodel.HSSFWorkbook;
    import org.apache.poi.ss.formula.functions.T;
    import org.apache.poi.ss.usermodel.Cell;
    import org.apache.poi.ss.usermodel.Row;
    import org.apache.poi.ss.usermodel.Sheet;
    import org.apache.poi.ss.usermodel.Workbook;
    import org.apache.poi.xssf.usermodel.XSSFWorkbook;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    
    import java.io.FileInputStream;
    import java.io.InputStream;
    import java.lang.reflect.Field;
    import java.text.DecimalFormat;
    import java.text.SimpleDateFormat;
    import java.util.ArrayList;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;
    
    /**
     * Author xz
     * Date 2018/6/11、9:18
     * Version 1.0
     **/
    public class ImportExcelFileUtil {
        private static final Logger log = LoggerFactory.getLogger(ImportExcelFileUtil.class);
        private final static String excel2003L =".xls";    //2003- 版本的excel
        private final static String excel2007U =".xlsx";   //2007+ 版本的excel
    
        /**
         * 拼装单个obj  通用
         *
         * @param obj
         * @param row
         * @return
         * @throws Exception
         */
        private  static Map<String, Object> dataObj(Object obj, Row row) throws Exception {
            Class<?> rowClazz= obj.getClass();
            Field[] fields = FieldUtils.getAllFields(rowClazz);
            if (fields == null || fields.length < 1) {
                return null;
            }
            //容器
            Map<String, Object> map = new HashMap<String, Object>();
            //注意excel表格字段顺序要和obj字段顺序对齐 (如果有多余字段请另作特殊下标对应处理)
            for (int j = 0; j < fields.length; j++) {
                map.put(fields[j].getName(), getVal(row.getCell(j)));
            }
            return map;
        }
        /**
         * 处理val
         *
         * @param cell
         * @return
         */
        public static String getVal(Cell cell) {
            Object value = null;
            DecimalFormat df = new DecimalFormat("0");  //格式化字符类型的数字
            SimpleDateFormat sdf = new SimpleDateFormat("yyy-MM-dd");  //日期格式化
            DecimalFormat df2 = new DecimalFormat("0.00");  //格式化数字
            switch (cell.getCellType()) {
                case Cell.CELL_TYPE_STRING:
                    value = cell.getRichStringCellValue().getString();
                    break;
                case Cell.CELL_TYPE_NUMERIC:
                    if("General".equals(cell.getCellStyle().getDataFormatString())){
                        value = df.format(cell.getNumericCellValue());
                    }else if("m/d/yy".equals(cell.getCellStyle().getDataFormatString())){
                        value = sdf.format(cell.getDateCellValue());
                    }else{
                        value = df2.format(cell.getNumericCellValue());
                    }
                    break;
                case Cell.CELL_TYPE_BOOLEAN:
                    value = cell.getBooleanCellValue();
                    break;
                case Cell.CELL_TYPE_BLANK:
                    value = "";
                    break;
                default:
                    break;
            }
            return value.toString();
        }
        /**
         * * 读取出filePath中的所有数据信息
         *
         * @param filePath excel文件的绝对路径
         * @param obj
         * @return
         */
        public static List<Map<String, Object>> getDataFromExcel(String filePath, Object obj){
    
            if (null == obj) {
                return null;
            }
            List<Map<String, Object>> ret = null;
            FileInputStream fis =null;
            Workbook wookbook = null;
            int lineNum = 0;
            Sheet sheet = null;
            try{
                //获取一个绝对地址的流
                fis = new FileInputStream(filePath);
                wookbook = getWorkbook(fis,filePath);
                //得到一个工作表
                sheet = wookbook.getSheetAt(0);
                //获得表头
                Row rowHead = sheet.getRow(0);
                //列数
                int rows = rowHead.getPhysicalNumberOfCells();
                //行数
                lineNum = sheet.getLastRowNum();
                if(0 == lineNum){
                    log.info("ImportExcelFileUtil中的getDataFromExcel方法导入的Excel内没有数据!");
                }
                ret = getData(sheet, lineNum, rows, obj);
            } catch (Exception e){
                e.printStackTrace();
            }
            return ret;
        }
       
    
    
         * @param obj
         * @return
         */
        public static List<Map<String, Object>>  getData(Sheet sheet, int lineNum, int rowNum, Object obj){
            List<Map<String, Object>> ret = null;
            try {
                //容器
                ret = new ArrayList<Map<String, Object>>();
                //获得所有数据
                for(int i = 1; i <= lineNum; i++){
                    //获得第i行对象
                    Row row = sheet.getRow(i);
                    if(row!=null){
                        //装载obj
                        ret.add(dataObj(obj,row));
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
            return ret;
        }
    
        /**
         * 描述:根据文件后缀,自适应上传文件的版本
         *
         * @param inStr,fileName
         * @return
         * @throws Exception
         */
        public static Workbook getWorkbook(InputStream inStr, String fileName) throws Exception{
            Workbook wb = null;
            String fileType = fileName.substring(fileName.lastIndexOf("."));
            if(excel2003L.equals(fileType)){
                wb = new HSSFWorkbook(inStr);  //2003-
            }else if(excel2007U.equals(fileType)){
                wb = new XSSFWorkbook(inStr);  //2007+
            }else{
                throw new Exception("解析的文件格式有误!");
            }
            return wb;
        }
    
        public static void main(String[] args) throws Exception{
            ElUserInfoDTO dto = new ElUserInfoDTO();
            List<Map<String, Object>> dataFromExcel = getDataFromExcel("D:\\img\\测试4.xls", dto);
            for (int i = 0; i < dataFromExcel.size(); i++) {
                for (Map.Entry<String, Object> entry : dataFromExcel.get(i).entrySet()) {
                    System.out.println("Key = " + entry.getKey() + ", Value = " + entry.getValue());
                }
            }
            System.out.println(dataFromExcel);
        }
    }
    

    (3)创建多线程,并计算线程数,此实现的线程是Call,为了可以返回成功的结果

     public int importBatchData(List<Map<String, Object>> list,SelAgencyAndPostionVO selAgencyAndPostionVO) {
            //部门主键ID
            Long agencyId = selAgencyAndPostionVO.getAgencyId();
            //类型ID(1:岗位 2:人员 )
            Integer typeId = selAgencyAndPostionVO.getTypeId();
            //岗位主键ID
            Long postionId = selAgencyAndPostionVO.getPostionId();
    
            int row = 0;
            try {
                if (typeId == 1) {
                    row = savePositionInfoList(list,agencyId);
                } else if (typeId == 2) {
                    Long orgId = elAppInfoService.getOrg().getOrgId();
                   //在导入之前,把同orgId下的用户全部放进缓存,防止重复导入
                    ElUserInfo elUserInfo = new ElUserInfo();
                    elUserInfo.setOrgId(orgId);
                    List<ElUserInfo> users = userInfoMapper.getUsersByOrgId(elUserInfo);
                    //Redis的key值
                    String userCodeKey = AppConstants.Flag.USERKEY + orgId;
                    //存入Redis之前,把之前的清空
                    redisCacheService.deleteRedisCacheByKey(userCodeKey);
                    //规则:key==>"userCode"+orgId,value==>users   存入Redis
                    redisCacheService.setRedisCacheByKey(userCodeKey,JSON.toJSONString(users),3L,TimeUnit.MINUTES);
                    row = saveUserInfoList(list,agencyId,postionId);
                }
    
            } catch (Exception e) {
                e.printStackTrace();
            }
            return row;

     

     public int saveUserInfoList(List<Map<String, Object>> list, Long agencyId, Long postionId) {
            Integer row = 1;
            Integer successCount = 0;
            int count = 50;// 一个线程处理50条数据
            int listSize = list.size();// 数据集合大小
            int runThreadSize = (listSize / count) + 1; // 开启的线程数
            List<Map<String, Object>> newlist = null;// 存放每个线程的执行数据
            ExecutorService executor = Executors.newFixedThreadPool(runThreadSize);// 创建一个线程池,数量和开启线程的数量一样
    
            // 创建两个个计数器
            CountDownLatch begin = new CountDownLatch(1);
            CountDownLatch end = new CountDownLatch(runThreadSize);
            // 循环创建线程
            for (int i = 0; i < runThreadSize; i++) {
                if ((i + 1) == runThreadSize) {
                    int startIndex;
                    startIndex = (i * count);
                    int endIndex = list.size();
                    newlist = list.subList(startIndex, endIndex);
                } else {
                    int startIndex = (i * count);
                    int endIndex = (i + 1) * count;
                    newlist = list.subList(startIndex, endIndex);
                }
    
                //线程类,处理数据持久化
                UserInfoThread userInfoThread = new UserInfoThread(newlist,begin,end,agencyId,postionId);
                //executor.execute(userInfoThread);
                Future<Integer> submit = executor.submit(userInfoThread);
                try {
                    //提交成功的次数
                    row = submit.get();
                    successCount += row;
                } catch (InterruptedException e1) {
                    log.error("ElAgencyPositionUserServiceImpl的saveUserInfoList方法error"+e1.getMessage(),e1);
                } catch (ExecutionException e2) {
                    log.error("ElAgencyPositionUserServiceImpl的saveUserInfoList方法error"+e2.getMessage(),e2);
                }
            }
            try{
                begin.countDown();
                end.await();
                //执行完关闭线程池
                executor.shutdown();
            }catch (Exception e) {
                log.error("ElAgencyPositionUserServiceImpl的saveUserInfoList方法error"+e.getMessage(),e);
            }
            return successCount;
        }

    (4)UserInfoThread具体实现业务

    package com.xxx.service;
    
    import com.alibaba.fastjson.JSON;
    import com.xxx.utils.redis.RedisCacheService;
    import com.xxx.bean.ElUserInfo;
    import com.xxx.common.AppConstants;
    import com.xxx.dao.UserInfoMapper;
    import com.xxx.utils.SpringUtil;
    import org.apache.commons.collections.CollectionUtils;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.util.StringUtils;
    
    import java.util.ArrayList;
    import java.util.Date;
    import java.util.List;
    import java.util.Map;
    import java.util.concurrent.Callable;
    import java.util.concurrent.CountDownLatch;
    
    import static com.xxx.utils.MD5Utils.MD5;
    
    /**
     * Author xz
     * Date 2018/6/11、17:24
     * Version 1.0
     **/
    public class UserInfoThread implements Callable<Integer> {
    
        private static final Logger log = LoggerFactory.getLogger(PostionThread.class);
        private List<Map<String, Object>> list;
        private CountDownLatch begin;
        private CountDownLatch end;
        private Long agencyId;
        private Long postionId;
        private UserInfoMapper userInfoMapper;
        private OrgEmployeeService orgEmployeeService;
        private RedisCacheService redisCacheService;
        private ElAppInfoService elAppInfoService;
    
        //创建个构造函数初始化 list,和其他用到的参数
        public UserInfoThread(List<Map<String, Object>> list,CountDownLatch begin,CountDownLatch end, Long agencyId, Long postionId){
            this.list = list;
            this.begin = begin;
            this.end = end;
            this.agencyId = agencyId;
            this.postionId = postionId;
            userInfoMapper = (UserInfoMapper)SpringUtil.getBean("userInfoMapper");
            orgEmployeeService = (OrgEmployeeService)SpringUtil.getBean(OrgEmployeeService.class);
            redisCacheService = (RedisCacheService)SpringUtil.getBean(RedisCacheService.class);
            elAppInfoService = (ElAppInfoService)SpringUtil.getBean(ElAppInfoService.class);
    
        }
    
        @Override
        public Integer call(){
            int row = 0;
            try {
                List<ElUserInfo>  userList = new ArrayList<ElUserInfo>();
                if (CollectionUtils.isNotEmpty(list)) {
                    //组织id
                    Long orgId = elAppInfoService.getOrg().getOrgId();
                    A:for (int i = 0; i < list.size(); i++) {
                        Map<String, Object> map = list.get(i);
                        String userSex = map.get("userSex").toString().trim();
                        String userName = map.get("userName").toString().trim();
                        String userTel = map.get("userTel").toString().trim();
                        String passWord = map.get("passWord").toString().trim();
                        String key = AppConstants.Flag.USERKEY+orgId;
    
                        //导入的人员信息字段有一个是为空的,就不持久化数据库。
                        if (StringUtils.isEmpty(userSex)) {
                            continue;
                        }
                        if (StringUtils.isEmpty(userName)) {
                            continue;
                        }
                        if (StringUtils.isEmpty(userTel)) {
                            continue;
                        }
                        if (StringUtils.isEmpty(passWord)) {
                            continue;
                        }
    
                        //获取的是数据库存在的同orgId下用户信息,以json字符串形式
                        String userListValue = redisCacheService.getRedisCacheByKey(key);
                        //把json字符串转ElUserInfo用户对象
                        List<ElUserInfo> elUserInfos = JSON.parseArray(userListValue, ElUserInfo.class);
                        //去重,若有重复的就结束此次循环
                        for (ElUserInfo userInfo: elUserInfos) {
                            if (userTel.equals(userInfo.getUserTel())) {
                                continue A;
                            }
                        }
    
                        if ("男".equals(userSex)) {
                            userSex = "0";
                        } else if ("女".equals(userSex)){
                            userSex = "1";
                        }
                        ElUserInfo user = new ElUserInfo();
                        user.setUserName(userName);
                        user.setUserTel(userTel);
                        user.setPassWord(MD5(passWord));
                        user.setUserSex(userSex);
                        user.setPositionId(postionId);
                        user.setAgencyId(agencyId);
                        user.setCreateDate(new Date());
                        user.setUpdateDate(new Date());
                        user.setDelMark(0);
                        user.setRoleId(1L);
                        user.setEmployeeId(0L);
                        user.setOrgId(orgId);
                        userList.add(user);
                    }
                    if (CollectionUtils.isNotEmpty(userList)) {
                        //先持久化本地
                        row = userInfoMapper.createBatchUserInfoList(userList);
                        if (row > 0) {
                            //持久化成功后同步组织平台
                            String add = orgEmployeeService.addOrganRoleUserToPlatform(userList, "add");
                            if (!StringUtils.isEmpty(add) && !"-1".equals(add) && !"null".equals(add)) {
                                //同步成功后,修改OrgId和EmployeeId
                                for (ElUserInfo user : userList) {
                                    user.setEmployeeId(1L);
                                }
                                //以用户手机号码为唯一标示,批量修改OrgId和EmployeeId
                                userInfoMapper.updateBatchOrgId(userList);
                            }
                            log.info(this.getClass().getName()+"的UserInfoThread"+add.toString());
                        }
                    }
                }
                //....
                //执行完让线程直接进入等待
                // begin.await();
            } catch (Exception e) {
                log.error("elPositionInfoServiceImpl的UserInfoThread方法error"+e.getMessage(),e);
            }finally{
                //这里要主要了,当一个线程执行完了计数要减一不要这个线程会被一直挂起
                //,end.countDown(),这个方法就是直接把计数器减一的
                end.countDown();
            }
            return row;
        }
    }
    
    @Component
    public class SpringUtil implements ApplicationContextAware {
    
        private static ApplicationContext applicationContext;
    
        @Override
        public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
            if(SpringUtil.applicationContext == null) {
                SpringUtil.applicationContext = applicationContext;
            }
        }
    
        //获取applicationContext
        public static ApplicationContext getApplicationContext() {
            return applicationContext;
        }
    
        //通过name获取 Bean.
        public static Object getBean(String name){
            return getApplicationContext().getBean(name);
        }
    
        //通过class获取Bean.
        public static <T> T getBean(Class<T> clazz){
            return getApplicationContext().getBean(clazz);
        }
    
        //通过name,以及Clazz返回指定的Bean
        public static <T> T getBean(String name,Class<T> clazz){
            return getApplicationContext().getBean(name, clazz);
        }
    
    }

    (5)最后,欢迎大家关注【码农新锐】公众号,加入我们一起来进阶Java。

                                                                    

    展开全文
  • java多线程导入excel(poi)

    千次阅读 2019-06-04 12:20:00
    导入excel util 1 /** 2 * @Description: excel导入工具类 3 4 * @Author: hg 5 6 * @CreateDate: 2019/6/4 11:58 7 8 */ 9 @SuppressWarnings("all") 10 public class POIUtil<T...

    导入excel util

      1 /**
      2  * @Description: excel导入工具类
      3 
      4  * @Author: hg
      5 
      6  * @CreateDate: 2019/6/4 11:58
      7 
      8  */
      9 @SuppressWarnings("all")
     10 public class POIUtil<T> {
     11 
     12     private Logger logger = LoggerFactory.getLogger(this.getClass());
     13 
     14     private Workbook wb;
     15 
     16     private String[] property;
     17 
     18     private Class<T> c;
     19 
     20     private String filePath;
     21 
     22     private int startRow;
     23     
     24     private int startColumn;
     25 
     26     //是否需要hash
     27     private boolean isHash = false;
     28 
     29     //存放hash集合
     30     private HashSet<Integer> hashSet;
     31   
     32     private CopyOnWriteArrayList beanList;
     33 
     34     public CopyOnWriteArrayList getBeanList() {
     35         return beanList;
     36     }
     37 
     38     public POIUtil(String[] property, Class<T> c, String filePath, int startRow, int startColumn) {
     39         this.property = property;
     40         this.c = c;
     41         this.filePath = filePath;
     42         this.wb = getWorkbook(filePath);
     43         this.startRow = startRow;
     44         this.startColumn = startColumn;
     45         this.beanList = new CopyOnWriteArrayList();
     46     }
     47 
     48     /**
     49      * @Description:
     50      * @Param: [property 类的属性字符串数组, c class对下你给, filePath excel地址,
     51      * startRow 开始行, startColumn 开始列, hashSet 不需要必填,hash校验]
     52      * @return:
     53      * @Author: hg
     54      * @Date: 2019/6/4
     55      */
     56     public POIUtil(String[] property, Class<T> c, String filePath, int startRow, int startColumn, HashSet<Integer> hashSet) {
     57         this.property = property;
     58         this.c = c;
     59         this.filePath = filePath;
     60         this.wb = getWorkbook(filePath);
     61         this.startRow = startRow;
     62         this.startColumn = startColumn;
     63         this.hashSet = hashSet;
     64         this.isHash = true;
     65         this.beanList = new CopyOnWriteArrayList();
     66     }
     67 
     68     /**
     69      * @Description:
     70      * @Param: [startSheet sheet开始, endSheet sheet结束]
     71      * @return: void
     72      * @Author: hg
     73      * @Date: 2019/6/4
     74      */
     75     public void getBeanList(int startSheet, int endSheet) throws IllegalAccessException, InstantiationException, InvocationTargetException {
     76         HashMap<String, String> valueMap = new HashMap<>();
     77         for (int sheetNum = startSheet; sheetNum < endSheet; sheetNum++) {
     78             Sheet sheet = wb.getSheetAt(sheetNum);
     79             for (int rowNum = startRow; rowNum < sheet.getPhysicalNumberOfRows(); rowNum++) {
     80                 Row row = sheet.getRow(rowNum);
     81                 for (int i = 0, columnNum = startColumn; i < property.length; i++, columnNum++) {
     82                     Cell cell = row.getCell(columnNum);
     83                     valueMap.put(property[i], getCellValue(cell, cell.getCellType()));
     84                 }
     85                 //这里使用clone效率更高
     86                 T t = c.newInstance();
     87                 BeanUtils.populate(t, valueMap);
     88 
     89                 //校验非空值
     90                 try {
     91                     ValidatorUtils.validateEntity(t);
     92                 } catch (WLHSEException e) {
     93                     continue;
     94                 }
     95 
     96                 //hash校验
     97                 if (isHash) {
     98                     synchronized (hashSet) {
     99                         if (hashSet.contains(t.hashCode()))
    100                             continue;
    101                         hashSet.add(t.hashCode());
    102                     }
    103                 }
    104                 beanList.add(t);
    105             }
    106         }
    107     }
    108 
    109     private Workbook getWorkbook(String filePath) {
    110         Workbook wb = null;
    111         try {
    112             if (null != filePath) {
    113                 FileInputStream fis = new FileInputStream(filePath);
    114                 if (filePath.endsWith(".xls")) {
    115                     wb = new HSSFWorkbook(fis);
    116                 } else if (filePath.endsWith(".xlsx")) {
    117                     wb = new XSSFWorkbook(fis);
    118                 }
    119                 return wb;
    120             }
    121         } catch (Exception e) {
    122             logger.error(e.getMessage(), e);
    123         }
    124         return null;
    125     }
    126 
    127     private String getCellValue(Cell rowCell, int rowCellType) {
    128         String value = "";
    129         switch (rowCellType) {
    130             case Cell.CELL_TYPE_STRING:
    131                 value = rowCell.getStringCellValue();
    132                 break;
    133             case Cell.CELL_TYPE_NUMERIC:
    134                 String dataFormat = rowCell.getCellStyle().getDataFormatString();
    135                 AtomicReference<Boolean> isDate = new AtomicReference<>(false);
    136                 if (DateUtil.isCellDateFormatted(rowCell)) {
    137                     value = new SimpleDateFormat("yyyy-MM-dd").format(DateUtil.getJavaDate(rowCell.getNumericCellValue()));
    138                 } else if (DateUtil.isCellInternalDateFormatted(rowCell)) {
    139                     value = new SimpleDateFormat("yyyy-MM-dd").format(DateUtil.getJavaDate(rowCell.getNumericCellValue()));
    140                 } else if (isDate.get()) {
    141                     value = new SimpleDateFormat("yyyy-MM-dd").format(rowCell.getDateCellValue());
    142                 } else if (dataFormat == null) {
    143                     value = new SimpleDateFormat("yyyy-MM-dd").format(DateUtil.getJavaDate(rowCell.getNumericCellValue()));
    144                 } else {
    145                     if (dataFormat != null) {
    146                         value = String.valueOf(rowCell.getNumericCellValue());
    147                     } else {
    148                         if (rowCell.getCellStyle().getDataFormatString().contains("$")) {
    149                             value = "$" + rowCell.getNumericCellValue();
    150                         } else if (rowCell.getCellStyle().getDataFormatString().contains("¥")) {
    151                             value = "¥" + rowCell.getNumericCellValue();
    152                         } else if (rowCell.getCellStyle().getDataFormatString().contains("¥")) {
    153                             value = "¥" + rowCell.getNumericCellValue();
    154                         } else if (rowCell.getCellStyle().getDataFormatString().contains("€")) {
    155                             value = "€" + String.valueOf(rowCell.getNumericCellValue());
    156                         } else {
    157                             value = String.valueOf(rowCell.getNumericCellValue());
    158                         }
    159                     }
    160                 }
    161                 break;
    162             case Cell.CELL_TYPE_BOOLEAN:
    163                 value = String.valueOf(rowCell.getBooleanCellValue());
    164                 break;
    165             case Cell.CELL_TYPE_ERROR:
    166                 value = ErrorEval.getText(rowCell.getErrorCellValue());
    167                 break;
    168             case Cell.CELL_TYPE_FORMULA:
    169                 value = rowCell.getCellFormula();
    170                 break;
    171         }
    172         return value;
    173     }
    174 
    175     public Workbook getWb() {
    176         return wb;
    177     }
    178 }

    导入excel线程

     1 public class POIThread implements Runnable {
     2     private int startSheet;
     3     private int endSheet;
     4     private POIUtil util;
     5     private CountDownLatch countDownLatch;
     6 
     7 
     8     POIThread(int startSheet, int endSheet, POIUtil util, CountDownLatch countDownLatch) {
     9         this.startSheet = startSheet;
    10         this.endSheet = endSheet;
    11         this.util = util;
    12         this.countDownLatch = countDownLatch;
    13     }
    14 
    15     @Override
    16     public void run() {
    17         try {
    18             util.getBeanList(startSheet, endSheet);
    19         } catch (IllegalAccessException | InstantiationException | InvocationTargetException e) {
    20             e.printStackTrace();
    21         } finally {
    22             countDownLatch.countDown();
    23         }
    24     }
    25 }

     

    执行线程

     1 public class POIThreadHelper {
     2     public void work(POIUtil util) {
     3         //一个sheet分配一个线程
     4         int runSize = util.getWb().getNumberOfSheets();
     5 
     6         ExecutorService executor = Executors.newFixedThreadPool(runSize);
     7         CountDownLatch countDownLatch = new CountDownLatch(runSize);
     8 
     9         for (int i = 0; i < runSize; i++) {
    10             executor.execute(new POIThread(i, i + 1, util,countDownLatch));
    11         }
    12 
    13         try {
    14             countDownLatch.await();
    15         } catch (InterruptedException e) {
    16             e.printStackTrace();
    17         }
    18         executor.shutdown();
    19     }
    20 }

     

    测试

     1 @Test
     2     public void test1() throws IllegalAccessException, InvocationTargetException, InstantiationException {
     3         String path = "C:\\logs\\test.xls";
     4         String[] test = {"name", "sex"};
     5         POIUtil<Student> poiUtil = new POIUtil<Student>(test, Student.class, path, 1, 0);
     6         POIThreadHelper helper = new POIThreadHelper();
     7         helper.work(poiUtil);
     8         for (Object o : poiUtil.getBeanList()) {
     9             System.out.println(o.toString());
    10         }
    11     }

     

     

     

     

    结果

     

    转载于:https://www.cnblogs.com/Gang-Bryant/p/10972818.html

    展开全文
  • java多线程导入excel

    万次阅读 2021-03-26 09:17:57
    import com.alibaba.excel.util.StringUtils; import com.supcon.mare.common.util.exception.InterfaceException; import com.supcon.mare.tankinfo.constant.Constants; import ...
  • 废话少说,直接上代码。 BaseMapper.xml &lt;!-- batchInsert --&...java.util.List"&gt; insert into base (name,password) values &lt;foreach collection="lis...
  • 多线程处理Excel导入数据入库

    千次阅读 2020-04-04 23:18:23
    springboot整合mybatisPlus,模拟导入2.4万条数据。 一、目录结构如下图 二、依赖 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-...
  • java多线程处理Excel

    2016-01-11 19:06:18
    package net.winbox.util; import java.io.BufferedInputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.InputStream; import java.sql
  • 导入Excel数据,验证数据是否符合标准,如果不符合则返回所有错误的行数,数据量每个Excel为2w~3w左右数据量,导入较慢,通过线程池把数据分块为每1000条为一批数据,同时导入数据库提高速度。 二、Excel文件标准 ...
  • springboot开的excel导入数据,并把数据插入到数据库操作
  • java 多线程处理导入数据

    千次阅读 2016-12-26 11:30:07
    获取导入excel文件中的数据,存入list中进行多线程处理,设定每个线程处理多少条数据,进行设定值动态启动线程数量数据,根据线程处理 数量拆分动态list数据进行相关业务操作 package com.ig.bi.game.controller; ...
  • 1.Excel数据: 数据说明:第一条数据完整,可以成功导入;第二条数据无姓名,业务逻辑姓名不允许为空,会导出到错误Excel中;第三条数据无姓名无类型,业务逻辑姓名类型不能为空,会导出错误Excel中。 2.导入...
  • 对大数据量的导出excel,用多线程,用倒数计数器对文件进行生成,使用poi,可以支持大数据量的生成,项目中使用的poi是3.1的,上传的是4.1的。
  • Java POI 多线程导入导出

    千次阅读 2019-10-17 10:28:24
    1.多线程导入导出。 导出的话是用JAVA POI excel导出,其中遇到的问题有两点。 其一:大量数据的导出 解决方案:用SXSSFWorkbook新excel可以导出超过6.5w条数据,而用03版的excel只能导出低于6.5w条的数据 其二:...
  • 因业务需求要实现数据的批量导入,给了指定的excel模板上传文件,用java代码实现导入excel文件。 重新复习下。 下面是源码,仅供参考。 controller层代码,创建临时目录后,使用DiskFileItemFactory类来实现...
  • 基于SpringBoot的多线程实现快速导入EXCEL

    千次阅读 热门讨论 2019-04-19 18:57:31
    此次出一版基于SpringBoot的多线程实现快速导入EXCEL,效率之高体现在速度之快,单次解析的数据量之大。对比如下: 第一版:导入5万条数据,花费近一个时 第二版:导入10万条数据,花费23分钟,优化后达到了15分钟...
  • 获取导入excel文件中的数据,存入list中进行多线程处理,设定每个线程处理多少条数据,进行设定值动态启动线程数量数据,根据线程处理 数量拆分动态list数据进行相关业务操作 package com.ig.bi.game.controller; ...
  • java导入大数据(多线程导入

    千次阅读 2021-06-10 11:38:13
    // 多线程插入 final CyclicBarrier barrier = new CyclicBarrier(maxThreadNums + 1); ExecutorService executorService = Executors.newFixedThreadPool(maxThreadNums); executorService.execute(new Runnable()...
  • EXCEL解析导入,批量插入大量数据 excel导入基本分为3个步骤: 1. 上传文件 ...2. 多线程执行 直接贴代码,项目实例: public Result importExcel1() { AmroContext context = AmroContextUtil.get
  • Java使用多线程导入数据到Oracle中

    千次阅读 2016-05-20 21:27:44
    Java使用多线程批量导入数据到Oracle中
  • Semaphore :java并发的核心 Semaphore 是 synchronized 的加强版,作用是控制线程的并发数量。 1.工作原理 以一个停车场是运作为例。为了简单起见,假设停车场只有三个车位,一开始三个车位都是空的。这时如果同时...
  • java线程异步导出excel

    千次阅读 2019-08-19 17:33:56
    现在用的比较的都是用POI技术来导出Excel,对于导出数据量不大的直接用POI技术按部就班实现即可,但是对于数据量大且需要导出的数据封装业务较复杂,单纯的用POI技术实现在高并发导出数据量较大时就会出现应用程序...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 4,864
精华内容 1,945
关键字:

java多线程导入excel数据

java 订阅