使用poi的SXSSF实现复杂的excel表格样式导出(使用多线程 向同一个sheet写入 每写入一行一个线程)

1.为什么使用SXSSF,而不使用XSSF或HSSF?

2.使用多线程操作poi时,需注意的事项。

  • sheet.creaRow() 方法是非线程安全的 ,需要进行控制
  • poi 若需要对每个单元格分别设置样式,则不要每次的创建(cell.createCellStyle()),否则很可能会出现样式混乱情况,正确做法应该复用相同单元格的样式,即使用 cellStyle.cloneStyleFrom(srcCellStyle)方法

3.代码

1.控制层(controller)
    /**
     * 导出EXCEL表
     * @param request
     * @param response
     */
    @GetMapping(value = "/getExport.do")
    public void getExportRcxc(HttpServletRequest request, HttpServletResponse response){
        String date = request.getParameter("startTime");
        String isUpdate = request.getParameter("isUpdate");

        //传入日期的 月初日期
        String startTime = DateUtils.getMonthBegin(date);
        //传入日期的 月末日期
        String endTime = DateUtils.getMonthEnd(date);
        String returnName ="XXXX统计表";
        try {
            ByteArrayOutputStream excelData = getExcelData(startTime,date,endTime, isUpdate);
            if(Objects.nonNull(excelData)){
                DownloadUtils.downloadExcel(excelData,response,returnName);
            }else {
                response.setStatus(201);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
/**
     * 生成表格数据
     * @param startTime 月初时间
     * @param date 当前时间
     * @param endTime   月末时间
     * @return
     * @throws IOException
     */
    private ByteArrayOutputStream getExcelData(String startTime,String date,String endTime, String isUpdate) throws IOException {
        Instant startT1 = Instant.now();
        //1.从数据库中获取查询日期下的数据
        List<Map> dataList = getData(startTime,date, endTime, isUpdate);
        Instant endT1 = Instant.now();
        System.out.println("导出功能---获取数据库所需时间:"+Duration.between(startT1,endT1).toMillis()+"毫秒");

        Instant startT2 = Instant.now();
        //2.生成 excel 表格
        // 2.1 创建工作簿
        SXSSFWorkbook workbook =  MergeExcelUtils.creatMyXSSFWorkbook();
        if(null != dataList && dataList.size()>0){
            //2.1定义工作表
            SXSSFSheet mySheet = MergeExcelUtils.createMySheet(workbook, "XXXX统计表", true, 4, 4);
            //sheet设置为无限制访问
            mySheet.setRandomAccessWindowSize(-1);

            //2.3.定义单元格样式
            //定义标题单元格样式
            XSSFCellStyle titleStyle = MergeExcelUtils.setCellStyle(workbook, "仿宋", 16, true, true, false, true, IndexedColors.WHITE.getIndex(),null);
            //奇数动态移动的表头单元格样式
            XSSFCellStyle dynamic1HeadStyle = MergeExcelUtils.setCellStyle(workbook, "仿宋", 11, true, true, true, true, IndexedColors.SKY_BLUE.getIndex(),null);
            //偶数动态移动的表头单元格样式
            XSSFCellStyle dynamic2HeadStyle = MergeExcelUtils.setCellStyle(workbook, "仿宋", 11, true, true, true, true, IndexedColors.PINK.getIndex(),null);
            //固定的表头单元格样式
            XSSFCellStyle fixedHeadStyle = MergeExcelUtils.setCellStyle(workbook, "仿宋", 11, true, true, true, true, IndexedColors.WHITE.getIndex(),null);

            //2.4.标题行
            String titleRowCellValue ="我是大标题行";
            SXSSFRow row0 = (SXSSFRow) mySheet.createRow(0);
            MergeExcelUtils.setMyRow(mySheet, titleStyle, row0, 0, 47, titleRowCellValue, new CellRangeAddress(0, 0, 0, 20));

            //2.5 定义生成表头
            //map 的key值 这里的key 要对应excel 显示的字段
            String keyArr[] = new String[]{
                    "a","b","c","d","e","f","g","h","i","j",
                    "k","l","m","n","o" ,"p" ,"q" ,"r"};
            Instant startT2_1 = Instant.now();
            //获取时间list 这里用于实现excel表中复制标题行时改变时间值
            List<String> dateList = getDateList(dataList);
            Instant endT2_1 = Instant.now();
            System.out.println("导出功能---获取日期List所需时间:"+Duration.between(startT2_1,endT2_1).toMillis()+"毫秒");

            Instant startT2_2 = Instant.now();
            //需要复制的个数
            int copySize =(dateList.size())-1;
            //todo 暂时写死
//            //固定显示的行数 (如这几列 :序号	行1号	行2号	行3号 不随滚动条滚动)
//            int fixed = 4;
//            //动态的列数 (跟随时间动态增加的列)
//            int dyCol = 14;
            //表头所占的行数 (自己根据自己的表头所占用到的行数设置)
            int headRowNums = 3;
            //第一行的列名称
            String[] head0 = new String[]{"序号", "行1号", "行2号","行3号"};
            String[] head01 = new String[]{dateList.get(copySize), dateList.get(copySize), dateList.get(copySize),dateList.get(copySize), dateList.get(copySize),"列合并1", "列合并1", "列合并2", "列合并2", "列合并3", "列合并4", "列合并4", "列合并5","列合并5"};
            //第二行的列名称
            String[] head1 = new String[]{" ", " ", " ", " "};
            String[] head11 = new String[]{"行4号", "行5号", "行6号", "行7号", "行8号", " ", " ", " ", " ", " ", " ", " ", " "," "};
            //第三行的列名称
            String[] head2 = new String[]{" ", " ", " ", " "};
            String[] head21 = new String[]{" ", " ", " ", " ", " ", "z1", "z2", "z3", "z4", "z5", "z6", "z7", "z8","z9"};
            //对应excel表格中的行和列 ,形式为("开始行,结束行,开始列,结束列")
            //第一行的合并行列
            String[] mergeCoordinate0 = new String[]{"1,3,0,0", "1,3,1,1,", "1,3,2,2", "1,3,3,3"};
            String[] mergeCoordinate01 = new String[]{"1,1,4,8","1,2,9,10","1,2,11,12","1,2,13,13","1,2,14,15","1,2,16,17"};
            //第二行的合并行列
            String[] mergeCoordinate11 = new String[]{ "2,3,4,4","2,3,5,5","2,3,6,6","2,3,7,7","2,3,8,8"};
            //组装表格头部Map<行号,对应的列值(列名值和合并的坐标值)>
            Map<Integer, Map<String, String[]>> headMap = Maps.newHashMap();
            Map<String, String[]> head0Map = new HashMap<>();
            head0Map.put(MergeExcelUtils.HEADNAME, head0);
            head0Map.put(MergeExcelUtils.MERGECOORDINATE, mergeCoordinate0);
            Map<String, String[]> head1Map = new HashMap<>();
            head1Map.put(MergeExcelUtils.HEADNAME, head1);
            Map<String, String[]> head2Map = Maps.newHashMap();
            head2Map.put(MergeExcelUtils.HEADNAME, head2);
            //headMap值 对应 1,2,3 行
            headMap.put(1, head0Map);
            headMap.put(2, head1Map);
            headMap.put(3, head2Map);
            Map<Integer, Map<String, String[]>> headMap11 = Maps.newHashMap();
            Map<String, String[]> head01Map = new HashMap<>();
            head01Map.put(MergeExcelUtils.HEADNAME, head01);
            head01Map.put(MergeExcelUtils.MERGECOORDINATE, mergeCoordinate01);
            Map<String, String[]> head11Map = new HashMap<>();
            head11Map.put(MergeExcelUtils.HEADNAME, head11);
            head11Map.put(MergeExcelUtils.MERGECOORDINATE, mergeCoordinate11);
            Map<String, String[]> head21Map = Maps.newHashMap();
            head21Map.put(MergeExcelUtils.HEADNAME, head21);
            //headMap11值 对应 1,2,3 行
            headMap11.put(1, head01Map);
            headMap11.put(2, head11Map);
            headMap11.put(3, head21Map);
            //设置固定表头
            MergeExcelUtils.setFixedHead(mySheet,fixedHeadStyle,headMap);
            //设置动态表头
            MergeExcelUtils.setDynamicHead(mySheet,dynamic1HeadStyle,headMap11,4);
            //复制表格标题
            for (int i = 1; i <= copySize; i++) {
                int pPosition  =18+((i-1)*14);
                int dateIndex = copySize-i;
                String headDate = dateList.get(dateIndex);
                if(i%2==0){
                    MergeExcelUtils.copyCols(mySheet,5,18,pPosition,headRowNums,dynamic1HeadStyle,headDate,4,8);
                }else{
                    MergeExcelUtils.copyCols(mySheet,5,18,pPosition,headRowNums,dynamic2HeadStyle,headDate,4,8);
                }
            }
            Instant endT2_2 = Instant.now();
            System.out.println("导出功能---生成excel表头所需时间:"+Duration.between(startT2_2,endT2_2).toMillis()+"毫秒");
            //2.6 对数据进行校验判断
            Instant startT2_3 = Instant.now();
            List<List<Map>>  list = checkRule(dataList);
            Instant endT2_3 = Instant.now();
            System.out.println("导出功能---校验生成excel数据所需时间:"+Duration.between(startT2_3,endT2_3).toMillis()+"毫秒");
            //2.7 设置excel表数据
            Instant startT2_4 = Instant.now();
            setExcelDataByManyThread(workbook,mySheet,list,keyArr);
            Instant endT2_4 = Instant.now();
            System.out.println("导出功能---生成excel数据所需时间:"+Duration.between(startT2_4,endT2_4).toMillis()+"毫秒");
        }
        Instant endT2 = Instant.now();
        System.out.println("导出功能---生成excel总所需时间:"+Duration.between(startT2,endT2).toMillis()+"毫秒");
        //3.将工作簿写入流
        ByteArrayOutputStream result =new ByteArrayOutputStream();
        workbook.write(result);
        //4.删除产生的临时文件
        workbook.dispose();
        return result;
    }
 /**
     * 获取数据库数据并按逻辑进行封装
     * @param startTime 月初时间
     * @param date 当前时间
     * @param endTime   月末时间
     * @return
     */
    private List<Map> getData(String startTime,String date,String endTime, String isUpdate){
        //当前系统时间
        String now = DateUtils.parseDateToString(LocalDate.now());
        List<Map> dataResult = new ArrayList<>();

        //判断当前系统时间是否在startTime 和 endTime 之内,是,获取当天的数据,否则直接获取历史数据
        if(DateUtils.beforeDate(LocalDate.now(), DateUtils.convertStr2LocalDate(endTime))){//当月内
            int num = insert(now,isUpdate);
        }

        dataResult = rcxcService.getExportRcxcDataInfo(startTime, endTime);//获取数据
        return dataResult;
    }
    /**
     * 多线程 填充excel 数据
     * @param workbook  工作簿对象
     * @param mySheet   工作表对象
     * @param dataList  数据
     * @param keyArr    需要校验的字段key
     */
    private void setExcelDataByManyThread(SXSSFWorkbook workbook,SXSSFSheet mySheet,List<List<Map>> dataList,String[] keyArr){
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        // 设置核心线程数
        executor.setCorePoolSize(8);
        // 设置最大线程数
        executor.setMaxPoolSize(8);
        // 设置队列容量
        executor.setQueueCapacity(350);
        // 设置线程活跃时间(秒)
        executor.setKeepAliveSeconds(60);
        // 等待所有任务结束后再关闭线程池
        executor.setWaitForTasksToCompleteOnShutdown(true);
        // rejection-policy:当pool已经达到max size的时候,如何处理新任务
        // CALLER_RUNS:不在新线程中执行任务,而是有调用者所在的线程来执行
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        executor.initialize();
        int t = Runtime.getRuntime().availableProcessors();
        System.out.println("当前可用的线程数:"+t);

        BlockingQueue<List<List<Map>>> queue = new ArrayBlockingQueue(dataList.size());
        //以先入先出的顺序排序的阻塞队列
        for (int i = 0; i < dataList.size(); i++){
            List<List<Map>> listMap = new ArrayList<>();
            List<Map> mapList = dataList.get(i);
            listMap.add(mapList);
            queue.add(listMap);
        }
        //设置单元格的样式
        List<XSSFCellStyle> cellStyles = new ArrayList<>();
        XSSFCellStyle normalCellStyle = MergeExcelUtils.setCellStyle(workbook, "仿宋", 11, true, true, true, true, IndexedColors.WHITE.getIndex(),null);
        XSSFCellStyle greenStyle = MergeExcelUtils.setCellStyle(workbook,"仿宋", 11, true, true, false, true, IndexedColors.GREEN.getIndex(),null);
        XSSFCellStyle yellowStyle = MergeExcelUtils.setCellStyle(workbook,"仿宋", 11, true, true, false, true, IndexedColors.YELLOW.getIndex(),null);
        XSSFCellStyle redStyle = MergeExcelUtils.setCellStyle(workbook,"仿宋", 11, true, true, false, true, IndexedColors.RED.getIndex(),null);
        cellStyles.add(greenStyle);
        cellStyles.add(yellowStyle);
        cellStyles.add(redStyle);
        cellStyles.add(normalCellStyle);

        CountDownLatch latch = new CountDownLatch(queue.size());
        int rowNum = 0;
        while (queue.size()>0){
            try {
                rowNum++;
                executor.execute(new SetExcelValueTask(latch,workbook,mySheet,cellStyles,queue.take(),rowNum, keyArr, 18,14));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        try {
            latch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    /**
     * 获取日期列表
     * @param dataList 数据
     * @return
     */
    private  List<String> getDateList(List<Map> dataList){
        LinkedHashMap<Object, List<Map>> gxsjCollect = dataList.stream().collect(Collectors.groupingBy(x -> x.get("XCSJ"), LinkedHashMap::new, Collectors.toList()));
        Set<Map.Entry<Object, List<Map>>> set = gxsjCollect.entrySet();
        Iterator<Map.Entry<Object, List<Map>>> iterator = set.iterator();
        List<String> dateList  = new ArrayList<>();
        while(iterator.hasNext()) {
            Map.Entry entry = iterator.next();
            String key = entry.getKey().toString();
            dateList.add(key);
        }
        return dateList;
    }

    /**
     * 给单元格设置值和颜色
     * @param workbook      工作簿
     * @param keyArr        表格显示字段的数组
     * @param keyIndex      表格显示字段的数组的下标
     * @param hashMap       返回的Map数据
     * @param sheet         工作表对象
     * @param row           行号
     * @param startIndex    开始循环遍历的行号
     * @param cols          循环遍历的列数
     */
    private void setValueAndColorToCell(SXSSFWorkbook workbook,String[] keyArr,int keyIndex,Map hashMap,SXSSFSheet sheet,int row ,int startIndex,int cols){
        XSSFCellStyle whiteStyle = MergeExcelUtils.setCellStyle(workbook, "仿宋", 11, true, true, false, true, IndexedColors.WHITE.getIndex(),null);
        XSSFCellStyle greenStyle = MergeExcelUtils.setCellStyle(workbook, "仿宋", 11, true, true, false, true, IndexedColors.GREEN.getIndex(),null);
        XSSFCellStyle yellowStyle = MergeExcelUtils.setCellStyle(workbook, "仿宋", 11, true, true, false, true, IndexedColors.YELLOW.getIndex(),null);
        XSSFCellStyle redStyle = MergeExcelUtils.setCellStyle(workbook, "仿宋", 11, true, true, false, true, IndexedColors.RED.getIndex(),null);

        for(int i = startIndex; i < cols; i++){
            String key = keyArr[keyIndex];
            String value = hashMap.get(key).toString();
            if(StringUtils.contains(value,"_green")){
                //使用绿色样式
                MergeExcelUtils.setMyCell(sheet, row, i, value.substring(0,value.lastIndexOf("_")),greenStyle);
            }else if(StringUtils.contains(value,"_yellow")){
                //使用黄色样式
                MergeExcelUtils.setMyCell(sheet, row, i, value.substring(0,value.lastIndexOf("_")),yellowStyle);
            }else if(StringUtils.contains(value,"_red")){
                //使用红色样式
                MergeExcelUtils.setMyCell(sheet, row, i, value.substring(0,value.lastIndexOf("_")),redStyle);
            }else{
                //使用默认的白色
                MergeExcelUtils.setMyCell(sheet, row, i, value,whiteStyle);
            }

            keyIndex++;
        }

    }



    /**
     * 进行数据校验
     * @param dataList
     * @return
     */
    private  List<List<Map>>  checkRule(List<Map> dataList){
        LinkedHashMap<Object, List<Map>> xzqhdmCollect = dataList.stream().collect(Collectors.groupingBy(x -> x.get("XZQHDM"), LinkedHashMap::new, Collectors.toList()));//使用 LinkedHashMap::new 只能保证其按原有顺序输出 ,所以要先做好准备工作
        ArrayList list = new ArrayList();
        //前一日与后一日比较的key
        String[] qhCompareKey =new String[]{"i","j","k","l","m","n","o" ,"p" ,"q" };
        //连续比较的key
        String[] lxCompareKey =new String[]{"d","e", "f", "g","h",};
        List<String[]> compareKey = new ArrayList<>();
        compareKey.add(qhCompareKey);
        compareKey.add(lxCompareKey);
        int xzqhdmCollectSize = xzqhdmCollect.size();
        for (int i = 0; i < xzqhdmCollectSize; i++){
            ArrayList data = (ArrayList) (xzqhdmCollect.values().toArray()[i]);
            List checkoutData = checkoutData(data, compareKey);
            list.add(checkoutData);
        }
        return list;
    }

    /**
     *  逻辑判断
     */
    private  List<Map> checkoutData(ArrayList<HashMap> dataList,List<String[]> compareKey){
        List<Map> listMap  = new ArrayList<>();
        int listSize = dataList.size();
        for(int i = listSize-1; i >= 0; i--){
            HashMap hashMap = dataList.get(i);
            //todo  相邻比较逻辑
            if((i-1) >= 0){
                int qhLen = compareKey.get(0).length;
                for(int n = 0; n< qhLen;n++){
                    String qhKey = compareKey.get(0)[n];
                    String currentStr = hashMap.get(qhKey).toString();
                    String nextStr = dataList.get(i-1).get(qhKey).toString();
                    int current = Integer.parseInt(currentStr);
                    int next = Integer.parseInt(nextStr);
                    int  diff = next - current;
                    if(diff<0){
                        //当前 大于 下一个 加“_green”标识
                        currentStr +="_green";
                        //保存值
                        hashMap.put(qhKey,currentStr);
                    }else if(diff>0){
                        if(diff>=3){
                            //当前 小于 下一个且相差值大于3  加“_red”标识
                            currentStr +="_red";
                        }else {
                            //当前 小于 下一个 加“_yellow”标识
                            currentStr +="_yellow";
                        }
                        //保存值
                        hashMap.put(qhKey,currentStr);
                    }
                }
            }
            if((i-2)>=0){
                //todo  连续三个工作日比较逻辑
                if((i-2>=0) && (i-6 < 0)){
                    int lxLen = compareKey.get(1).length;
                    for(int n = 0; n< lxLen;n++){
                        String lxKey = compareKey.get(1)[n];
                        String currentStr = hashMap.get(lxKey).toString();
                        String next2Str = dataList.get(i-1).get(lxKey).toString();
                        String next3Str = dataList.get(i-2).get(lxKey).toString();
                        String flag1 ="0";
                        //这个“平均时间间隔(分钟)” 比较特殊 为空时 返回 “0.0” 所以做特殊处理
                        String flag2 ="0.0";
                        if((StringUtils.equal(flag1,currentStr)
                                && StringUtils.equal(flag1,next2Str)
                                && StringUtils.equal(flag1,next3Str)) ||
                                (StringUtils.equal(flag2,currentStr)
                                        && StringUtils.equal(flag2,next2Str)
                                        && StringUtils.equal(flag2,next3Str))){
                            //加“_yellow”标识
                            currentStr +="_yellow";
                            //保存值
                            hashMap.put(lxKey,currentStr);
                        }
                    }
                }
                //todo  连续七个工作日比较逻辑
                if((i-6)>=0){
                    int lxLen = compareKey.get(1).length;
                    for(int n = 0; n< lxLen;n++){
                        String lxKey = compareKey.get(1)[n];
                        String currentStr = hashMap.get(lxKey).toString();
                        String next2Str = dataList.get(i-1).get(lxKey).toString();
                        String next3Str = dataList.get(i-2).get(lxKey).toString();
                        String next4Str = dataList.get(i-3).get(lxKey).toString();
                        String next5Str = dataList.get(i-4).get(lxKey).toString();
                        String next6Str = dataList.get(i-5).get(lxKey).toString();
                        String next7Str = dataList.get(i-6).get(lxKey).toString();

                        String flag1 ="0";
                        //这个“平均时间间隔(分钟)” 比较特殊 为空时 返回 “0.0” 所以做特殊处理
                        String flag2 ="0.0";
                        if((StringUtils.equal(flag1,currentStr)
                                && StringUtils.equal(flag1,next2Str)
                                && StringUtils.equal(flag1,next3Str)
                                && StringUtils.equal(flag1,next4Str)
                                && StringUtils.equal(flag1,next5Str)
                                && StringUtils.equal(flag1,next6Str)
                                && StringUtils.equal(flag1,next7Str)) ||
                                (StringUtils.equal(flag2,currentStr)
                                        && StringUtils.equal(flag2,next2Str)
                                        && StringUtils.equal(flag2,next3Str)
                                        && StringUtils.equal(flag2,next4Str)
                                        && StringUtils.equal(flag2,next5Str)
                                        && StringUtils.equal(flag2,next6Str)
                                        && StringUtils.equal(flag2,next7Str)) ){

                            //加“_red”标识
                            currentStr +="_red";
                            //保存值
                            hashMap.put(lxKey,currentStr);
                        }else {
                            if((i-2)>=0){
                                if((StringUtils.equal(flag1,currentStr)
                                        && StringUtils.equal(flag1,next2Str)
                                        && StringUtils.equal(flag1,next3Str)) ||
                                        (StringUtils.equal(flag2,currentStr)
                                                && StringUtils.equal(flag2,next2Str)
                                                && StringUtils.equal(flag2,next3Str))){
                                    //加“_yellow”标识
                                    currentStr +="_yellow";
                                    //保存值
                                    hashMap.put(lxKey,currentStr);
                                }
                            }
                        }
                    }
                }
            }
            listMap.add(hashMap);
        }
        return listMap;
    }
2.线程任务类
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.IndexedColors;
import org.apache.poi.xssf.streaming.SXSSFCell;
import org.apache.poi.xssf.streaming.SXSSFRow;
import org.apache.poi.xssf.streaming.SXSSFSheet;
import org.apache.poi.xssf.streaming.SXSSFWorkbook;
import org.apache.poi.xssf.usermodel.XSSFCellStyle;

import java.util.List;
import java.util.Map;
import java.util.concurrent.CountDownLatch;

/**
 *
 * 进行  导出excel时进行表值设置的线程任务
 */
public class SetExcelValueTask implements Runnable{
    private  CountDownLatch latch;
    private SXSSFWorkbook workbook;
    private SXSSFSheet sheet;
    private List<XSSFCellStyle> cellStyles;
    private List<List<Map>> dataList;
    private String[] keyArr;
    private int initCol;
    private int spanCol;
    private int rowNum;

    public RcxcSetExcelValueTask(CountDownLatch latch, SXSSFWorkbook workbook, SXSSFSheet sheet,List<XSSFCellStyle> cellStyles, List<List<Map>> dataList, int rowNum, String[] keyArr, int initCol, int spanCol) {

        this.latch = latch;
        this.workbook =workbook;
        this.sheet = sheet;
        this.cellStyles = cellStyles;
        this.dataList = dataList;
        this.keyArr = keyArr;
        this.initCol = initCol;
        this.spanCol = spanCol;
        this.rowNum = rowNum;
    }

    @Override
    public void run() {
        try {
            setExcelData(dataList,rowNum,  keyArr, initCol,  spanCol);
        }catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (null != latch) {
                latch.countDown();
            }
        }
    }

    /**
     * 设置 excel表值
     * @param dataList  数据
     * @param rowNum    行号
     * @param keyArr    表格显示字段的数组
     * @param initCol   初始跨越的列数
     * @param spanCol   动态跨越的列数
     */
    private  void setExcelData(List<List<Map>> dataList,int rowNum, String[] keyArr, int initCol, int spanCol){
        int listSizeSize = dataList.get(0).size();
        //序号样式
        XSSFCellStyle indexStyle = MergeExcelUtils.setCellStyle(workbook,"仿宋", 16, true, true, false, true, IndexedColors.WHITE.getIndex(),null);
        //从第5行开始
        int mm =3+rowNum;
        SXSSFRow rowi = MergeExcelUtils.createMyRow(sheet,mm);
        //序号
        MergeExcelUtils.setMyCell(sheet, mm,0, rowNum + "",indexStyle);
        int col=initCol;
        //列遍历
        for( int j = 0 ; j< listSizeSize; j++){
            int dyCol = spanCol;
            if(j == 0){
                setValueAndColorToCell(keyArr,0,dataList.get(0).get(j),mm,1,initCol);
            }else {
                int startIndex = col+(j-1)*dyCol;
                int endCol = col+((j)*dyCol);
                setValueAndColorToCell(keyArr,3,dataList.get(0).get(j),mm,startIndex,endCol);
            }
        }
    }

    /**
     *
     * @param keyArr        表格显示字段的数组
     * @param keyIndex      表格显示字段的数组的下标
     * @param hashMap       返回的Map数据
     * @param row           行对象
     * @param startIndex    开始循环遍历的行号
     * @param cols          循环遍历的列数
     */
    private void setValueAndColorToCell(String[] keyArr,int keyIndex,Map hashMap,int row ,int startIndex,int cols){
        for(int i = startIndex; i < cols; i++){
            String key = keyArr[keyIndex];
            String value = hashMap.get(key).toString();
            myManyThreadCell(sheet,cellStyles,row,i,value);
            keyIndex++;
        }

    }


    /**
     * 将为空、null、0、0.0的 值转换为 -(横杠)
     * @param value
     * @return
     */
    private String checkNullOrZeroReturnLine(String value){
        String result;
        if(StringUtils.equal("0",value) || StringUtils.equal("0.0",value) || StringUtils.isBlank(value)){
            result ="-";
        }else {
            result =value;
        }
        return result;
    }

    /**
     * 设置单元格
     * @param sheet         工作表对象
     * @param cellStyles    单元格样式
     * @param rowIndex      行号
     * @param colIndex      列号
     * @param vuale         单元格的值
     */
    private   void myManyThreadCell(SXSSFSheet sheet,List<XSSFCellStyle> cellStyles, int rowIndex, int colIndex, String vuale) {
        SXSSFRow myRow = MergeExcelUtils.createMyRow(sheet, rowIndex);
        SXSSFCell cell = (SXSSFCell) myRow.createCell(colIndex);
        myManyThreadCellStyle(vuale,cell,cellStyles);
    }

    /**
     * 具体设置单元格 样式和值
     * @param value         单元格的值
     * @param cell          单元格对象
     * @param cellStyles    单元格样式
     */
    private  void myManyThreadCellStyle(String value, SXSSFCell cell,List<XSSFCellStyle> cellStyles){
        if (StringUtils.contains(value,"_green")) {
            cell.setCellStyle(cellStyles.get(0));
            cell.setCellValue(checkNullOrZeroReturnLine(value.substring(0, value.lastIndexOf("_"))));
        }else if(StringUtils.contains(value,"_yellow")){
            cell.setCellStyle(cellStyles.get(1));
            cell.setCellValue(checkNullOrZeroReturnLine(value.substring(0, value.lastIndexOf("_"))));
        }else if(StringUtils.contains(value,"_red")){
            cell.setCellStyle(cellStyles.get(2));
            cell.setCellValue(checkNullOrZeroReturnLine(value.substring(0, value.lastIndexOf("_"))));
        }else {
            cell.setCellStyle(cellStyles.get(3));
            cell.setCellValue(checkNullOrZeroReturnLine(value));
        }

    }

}

3.使用到的工具类
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayOutputStream;
import java.io.IOException;

/**
 * 浏览器下载excel
 */
public class DownloadUtils {

    /**
     *浏览器端下载 excel方法
     * @param byteArrayOutputStream 输出流
     * @param response 响应对象
     * @param returnName 下载的文件名
     * @throws IOException
     */
    public static void downloadExcel(ByteArrayOutputStream byteArrayOutputStream, HttpServletResponse response, String returnName) throws IOException {
        response.setContentType("application/octet-stream");
        //保存的文件名,必须和页面编码一致,否则乱码
        returnName = response.encodeURL(new String(returnName.getBytes(),"iso8859-1"));

        response.addHeader("Content-Disposition","attachment;filename="+returnName+".xls");
        response.setContentLength(byteArrayOutputStream.size());
        response.addHeader("Content-Length", "" + byteArrayOutputStream.size());
        //取得输出流
        ServletOutputStream outputstream = response.getOutputStream();
        //写到输出流
        byteArrayOutputStream.writeTo(outputstream);
        //关闭
        byteArrayOutputStream.close();
        //刷数据
        outputstream.flush();
    }
}

import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.xssf.streaming.SXSSFCell;
import org.apache.poi.xssf.streaming.SXSSFRow;
import org.apache.poi.xssf.streaming.SXSSFSheet;
import org.apache.poi.xssf.streaming.SXSSFWorkbook;
import org.apache.poi.xssf.usermodel.*;

import java.util.Map;
import java.util.Objects;
import java.util.Set;

/**
 * 使用 SXSSF :  一种基于XSSF的低内存占用的API(3.8版本开始出现)。
 */
public class MergeExcelUtils {
    //列名称 key的值
    public static final String HEADNAME = "headName";
    //合并单元格坐标 key的值
    public static final String MERGECOORDINATE  = "mergeCoordinate";


    /**
     * 单元格的样式设置
     * @param fontName      字体
     * @param fontSize      字号
     * @param boldWeight    是否加粗 true为加粗 false为不加粗
     * @param showBorder    是否显示边框 true为显示 false为不显示
     * @param lineFeed      是否文字自动换行  true为自动换行 false为不自动换行
     * @param showBgColor   是否显示背景色  true为显示 false为不显示 注意:这个参数要配置colorValue参数使用
     * @param colorValue    设置背景的颜色值 当且仅当 showBgColor 为true时生效 注:colorValue 可以使用 IndexedColors类值 例如:IndexedColors.SKY_BLUE.getIndex()
     * @param txtColor      字体的颜色
     * @return
     */
    public  static synchronized   XSSFCellStyle setCellStyle(SXSSFWorkbook workbook,String fontName,int fontSize,boolean boldWeight,boolean showBorder,boolean  lineFeed,boolean showBgColor,short colorValue,Short txtColor){
        //1.单元格样式
        XSSFCellStyle style = (XSSFCellStyle) workbook.createCellStyle();
        //水平居中
        style.setAlignment(XSSFCellStyle.ALIGN_CENTER);
        //垂直居中
        style.setVerticalAlignment(XSSFCellStyle.VERTICAL_CENTER);
        //2.设置字体样式
        XSSFFont font  = (XSSFFont) workbook.createFont();
        //字体
        font.setFontName(fontName);
        //字号
        font.setFontHeightInPoints((short) fontSize);
        if(boldWeight){
            //加粗
            font.setBoldweight(XSSFFont.BOLDWEIGHT_BOLD);
        }
        //设置字体颜色
        if(txtColor!=null){
            font.setColor(txtColor);
        }
        style.setFont(font);
        //3.设置边框属性
        if(showBorder){
            //下边框
            style.setBorderBottom(XSSFCellStyle.BORDER_THIN);
            //左边框
            style.setBorderLeft(XSSFCellStyle.BORDER_THIN);
            //上边框
            style.setBorderTop(XSSFCellStyle.BORDER_THIN);
            //右边框
            style.setBorderRight(XSSFCellStyle.BORDER_THIN);
        }

        //4.设置自动换行 默认不换行
        if(lineFeed){
            style.setWrapText(true);
        }

        //5.设置背景色 不设置取默认空白
        if(showBgColor){
            //colorValue 可以使用 IndexedColors 例如:IndexedColors.SKY_BLUE.getIndex()
            style.setFillForegroundColor(colorValue);
            style.setFillPattern(CellStyle.SOLID_FOREGROUND);
        }

        return  style;
    }


    /**
     * 创建工作表
     * @param workbook          工作簿对象
     * @param sheetName         工作表对象
     * @param showFreezePane    是否显示固定单元格 true为显示 false为不显示 注意:要配合 col和row参数使用
     * @param col               固定的列数
     * @param row               固定的行数
     * @return
     */
    public static SXSSFSheet createMySheet(SXSSFWorkbook workbook, String sheetName, boolean showFreezePane, int col, int row){
        SXSSFSheet sheet = (SXSSFSheet) workbook.createSheet(sheetName);
        if(showFreezePane){
            //固定 row 行 ,col 列
            sheet.createFreezePane(col, row,col, row);
        }
        return sheet;
    }

    /**
     * 设置行内 单元格样式
     * @param sheet                 工作表
     * @param cellStyle             单元格样式
     * @param row                   行对象
     * @param rowCellIndex          行内的列号(从0开始)
     * @param heightInPoints        行高
     * @param rowCellValue          行内的列值
     * @param cellRangeAddress      合并单元格对象
     * @return
     */
    public  static void setMyRow(SXSSFSheet sheet, XSSFCellStyle cellStyle, SXSSFRow row, int rowCellIndex, int heightInPoints, String rowCellValue, CellRangeAddress cellRangeAddress){
        row.setHeightInPoints(heightInPoints);
        SXSSFCell cell = (SXSSFCell) row.createCell(rowCellIndex);
        cell.setCellStyle(cellStyle);
        cell.setCellValue(rowCellValue);
        if(Objects.nonNull(cellRangeAddress)){
            sheet.addMergedRegion(cellRangeAddress);
        }
    }


    /**
     * 设置固定表头
     * @param sheet 工作表对象
     * @param cellStyle 单元格样式
     * @param headMap 表头标题Map
     */
    public static void setFixedHead(SXSSFSheet sheet,XSSFCellStyle cellStyle,Map<Integer,Map<String, String[]>> headMap){
        Set<Integer> headKeySet = headMap.keySet();
        if (headKeySet.size() > 0) {
            headKeySet.stream().sorted();
            for (Integer rowNum : headKeySet) {
                Map<String, String[]> rowInfo = headMap.get(rowNum);
                String[] headName = rowInfo.get(HEADNAME);
                String[] mergeCoordinate = rowInfo.get(MERGECOORDINATE);
                //判断是否设置表头
                if (null != headName && headName.length > 0) {
                    Row row1 = sheet.createRow(rowNum);
                    for (int i = 0; i < headName.length; i++) {
                        Cell cell = row1.createCell(i);
                        cell.setCellValue(headName[i]);
                        cell.setCellStyle(cellStyle);
                    }
                }
                //判断是否需要合并
                addMyMergedRegion(sheet,mergeCoordinate);
            }
        }
    }

    /**
     * 设置动态的表头
     * @param sheet 工作表对象
     * @param cellStyle 单元格样式
     * @param headMap 表头标题Map
     * @param startCol 开始的列
     */
    public static void setDynamicHead(SXSSFSheet sheet,XSSFCellStyle cellStyle,Map<Integer,Map<String, String[]>> headMap,int startCol){
        Set<Integer> headKeySet = headMap.keySet();
        if (headKeySet.size() > 0) {
            headKeySet.stream().sorted();
            for (Integer rowNum : headKeySet) {
                Map<String, String[]> rowInfo = headMap.get(rowNum);
                String[] headName = rowInfo.get(HEADNAME);
                String[] mergeCoordinate = rowInfo.get(MERGECOORDINATE);
                //判断是否设置表头
                if (null != headName && headName.length > 0) {
                    Row row1 = sheet.getRow(rowNum);
                    for (int i = 0; i < headName.length; i++) {
                        Cell cell = row1.createCell(i+startCol);
                        cell.setCellValue(headName[i]);
                        cell.setCellStyle(cellStyle);
                    }
                }
                //判断是否需要合并
                addMyMergedRegion(sheet,mergeCoordinate);
            }
        }
    }
    /**
     * 创建工作簿
     * @return
     */
    public static SXSSFWorkbook creatMyXSSFWorkbook(){
        SXSSFWorkbook workbook = new SXSSFWorkbook();
        return workbook;
    }

    /**
     *复制单元格
     * @param currentSheet      工作表对象
     * @param startCol          开始的列号
     * @param endCol            结束的列号
     * @param pPosition         偏移量
     * @param headRowNums       标题标题的行数
     * @param cellStyle         单元格样式
     * @param date              日期值
     * @param setValueStartCol  设置日期值开始的列
     * @param setValueEndCol    设置日期值结束的列
     * @return
     */
    public static SXSSFSheet copyCols(SXSSFSheet currentSheet,int startCol, int endCol, int pPosition,int headRowNums,CellStyle cellStyle,String date,int setValueStartCol,int setValueEndCol) {

        int pStartCol= startCol - 1;
        int pEndCol = endCol - 1;
        int targetColFrom;
        int targetColTo;
        if (pStartCol == -1 || pEndCol == -1) {
            return null;
        }
        int mergedRegions = currentSheet.getNumMergedRegions();
        for (int i = 0; i < mergedRegions; i++) {
            CellRangeAddress region  = currentSheet.getMergedRegion(i);
            int firstColumn = region.getFirstColumn();
            int lastColumn = region.getLastColumn();
            if ((firstColumn >= pStartCol)&& ( lastColumn<= pEndCol)) {
                targetColFrom = firstColumn - pStartCol + pPosition;
                targetColTo = lastColumn - pStartCol + pPosition;
                CellRangeAddress newRegion = region.copy();
                newRegion.setFirstRow(region.getFirstRow());
                newRegion.setFirstColumn(targetColFrom);
                newRegion.setLastRow(region.getLastRow());
                newRegion.setLastColumn(targetColTo);
                currentSheet.addMergedRegion(newRegion);
            }
        }

        for (int i = 1; i <= headRowNums; i++) {
            int m = 0;
            SXSSFRow sourceRow = (SXSSFRow) currentSheet.getRow(i);
            if (sourceRow != null) {
                SXSSFRow newRow = (SXSSFRow) currentSheet.getRow(i);
                for (int j = pStartCol; j <= pEndCol; j++) {
                    SXSSFCell templateCell = (SXSSFCell) sourceRow.getCell(j);
                    int newCellIndex = pPosition + m;
                    int cellIndex = m + j;
                    currentSheet.setColumnWidth(newCellIndex, currentSheet.getColumnWidth(cellIndex));
                    if (templateCell != null) {
                        SXSSFCell newCell = (SXSSFCell) newRow.createCell(newCellIndex);
                        //todo 复制时改变日期
                        if( i==1 && j>=setValueStartCol &&j <= setValueEndCol){
                            newCell.setCellValue(date);
                        }
                        copyCell(templateCell, newCell,cellStyle);
                        m++;
                    }
                }
            }
        }
        return currentSheet;
    }

    /**
     * 复制单元格
     * @param srcCell   源单元格
     * @param distCell  目标单元格
     * @param cellStyle 新的单元格样式
     */
    public static void copyCell(SXSSFCell srcCell, SXSSFCell distCell,CellStyle cellStyle) {
        distCell.setCellStyle(srcCell.getCellStyle());
        distCell.setCellStyle(cellStyle);
        if (srcCell.getCellComment() != null) {
            distCell.setCellComment(srcCell.getCellComment());
        }
        int srcCellType = srcCell.getCellType();
        distCell.setCellType(srcCellType);
        if (srcCellType == XSSFCell.CELL_TYPE_NUMERIC) {
            distCell.setCellValue(srcCell.getNumericCellValue());
        } else if (srcCellType == XSSFCell.CELL_TYPE_STRING) {
            //todo 复制时 改变日期
            if(StringUtils.isBlank(distCell.getStringCellValue())){
                distCell.setCellValue(srcCell.getRichStringCellValue());
            }

        } else if (srcCellType == XSSFCell.CELL_TYPE_BLANK) {
        } else if (srcCellType == XSSFCell.CELL_TYPE_BOOLEAN) {
            distCell.setCellValue(srcCell.getBooleanCellValue());
        } else if (srcCellType == XSSFCell.CELL_TYPE_ERROR) {
            distCell.setCellErrorValue(srcCell.getErrorCellValue());
        } else if (srcCellType == XSSFCell.CELL_TYPE_FORMULA) {
            distCell.setCellFormula(srcCell.getCellFormula());
        } else {
        }
    }

    /**
     *设置单元格值
     * @param sheet         工作表对象
     * @param rowIndex      行号
     * @param colIndex      列号
     * @param vuale         值
     * @param cellStyle     样式
     */
    public  static void setMyCell(SXSSFSheet sheet, int rowIndex, int colIndex, String vuale, XSSFCellStyle cellStyle) {
        SXSSFRow myRow = createMyRow(sheet, rowIndex);
        SXSSFCell cell = (SXSSFCell) myRow.createCell(colIndex);
        cell.setCellValue(vuale);
        cell.setCellStyle(cellStyle);
    }


    /**
     * sheet的row使用treeMap存储的,是非线程安全的,所以在创建row时需要进行同步操作。
     * @param sheet
     * @param rownum
     * @return
     */
    public static synchronized SXSSFRow createMyRow(SXSSFSheet sheet, int rownum) {
        SXSSFRow row = (SXSSFRow) sheet.getRow(rownum);
        if(null != row){
            return row;
        }
        return (SXSSFRow) sheet.createRow(rownum);
    }

    /**
     * 添加的合并坐标
     * @param sheet 工作表
     * @param mergeCoordinate 合并的坐标
     */
    private static void addMyMergedRegion (SXSSFSheet sheet,String [] mergeCoordinate){
        //判断是否需要合并
        if (null != mergeCoordinate && mergeCoordinate.length > 0) {
            //动态合并单元格
            for (String mc : mergeCoordinate) {
                String[] temp = mc.split(",");
                Integer startrow = Integer.parseInt(temp[0]);
                Integer overrow = Integer.parseInt(temp[1]);
                Integer startcol = Integer.parseInt(temp[2]);
                Integer overcol = Integer.parseInt(temp[3]);
                CellRangeAddress cra = new CellRangeAddress(startrow, overrow, startcol, overcol);
                sheet.addMergedRegion(cra);
            }
        }
    }

}

import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.time.temporal.TemporalAdjusters;

/**
 * 使用java8的 时间工具类
 * 注:java8 相对之前版本的日期 它是线程安全的
 */
public class DateUtils {
    /**
     * 根据指定格式显示日期和时间
     */
    //1.yyyy-MM-dd
    private static final DateTimeFormatter yyyyMMdd = DateTimeFormatter.ofPattern("yyyy-MM-dd");

    /**
     * 通过传入的日期 获取该日期的月初时间
     * @param dateStr 日期
     * @return
     */
    public static String getMonthBegin(String dateStr) {
        return LocalDate.parse(dateStr, yyyyMMdd).with(TemporalAdjusters.firstDayOfMonth()).toString();
    }

    /**
     * 通过传入的日期 获取该日期的月末时间
     * @param dateStr 日期
     * @return
     */
    public static String getMonthEnd(String dateStr) {
        return LocalDate.parse(dateStr, yyyyMMdd).with(TemporalAdjusters.lastDayOfMonth()).toString();
    }

    /**
     * 日期转字符串
     * @param localDate 日期
     * @param pattern 日期格式
     * @return
     */
    public static String parseDateToString(LocalDate localDate,String pattern) {
        DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(pattern);
        return localDate.format(dateTimeFormatter);

    }

    /**
     * 日期转 yyyy-MM-dd 字符串格式
     * @param localDate 日期
     * @return
     */
    public static String parseDateToString(LocalDate localDate) {
        return localDate.format(yyyyMMdd);
    }


    /**
     * 当前日期减天数
     * @param localDate 日期
     * @param days 天数
     * @return
     */
    public static String changeLocalDateByDays(LocalDate localDate, int days){
        LocalDate date = localDate.minusDays(days);
        return parseDateToString(date);
    }

    /**
     *  字符串转 LocalDate 日期
     *  @param str 歹转换的字符串
     */
    public static LocalDate convertStr2LocalDate(String str){
        LocalDate localDate = LocalDate.parse(str, yyyyMMdd);
        return localDate;
    }

    /**
     * 判断两个日期的前后, 判断date1 是否在 date2 之前
     * @param date1 日期1
     * @param date2 日期2
     */
    public static boolean beforeDate(LocalDate date1, LocalDate date2){

        boolean flag = false;
        if(date1.isBefore(date2) || date1.equals(date2) ){
            flag = true;
        }
        return flag;
    }


}

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;

@Component
public class RedisUtils {

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    // =============================common============================
    /**
     * 指定缓存失效时间
     * @param key 键
     * @param time 时间(秒)
     * @return
     */
    public boolean expire(String key, long time) {
        try {
            if (time > 0) {
                redisTemplate.expire(key, time, TimeUnit.SECONDS);
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }
    /**
     * 根据key 获取过期时间
     * @param key 键 不能为null
     * @return 时间(秒) 返回0代表为永久有效
     */
    public long getExpire(String key) {
        return redisTemplate.getExpire(key, TimeUnit.SECONDS);
    }

    /**
     * 判断key是否存在
     * @param key 键
     * @return true 存在 false不存在
     */
    public boolean hasKey(String key) {
        try {
            return redisTemplate.hasKey(key);
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 删除缓存
     * @param key 可以传一个值 或多个
     */
    @SuppressWarnings("unchecked")
    public void del(String... key) {
        if (key != null && key.length > 0) {
            if (key.length == 1) {
                redisTemplate.delete(key[0]);
            } else {
                redisTemplate.delete(CollectionUtils.arrayToList(key));
            }
        }
    }
    // ============================String=============================
    /**
     * 普通缓存获取
     * @param key 键
     * @return 值
     */
    public Object get(String key) {
        return key == null ? null : redisTemplate.opsForValue().get(key);
    }

    /**
     * 普通缓存放入
     * @param key 键
     * @param value 值
     * @return true成功 false失败
     */
    public boolean set(String key, Object value) {
        try {
            redisTemplate.opsForValue().set(key, value);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 普通缓存放入并设置时间
     * @param key 键
     * @param value 值
     * @param time 时间(秒) time要大于0 如果time小于等于0 将设置无限期
     * @return true成功 false 失败
     */
    public boolean set(String key, Object value, long time) {
        try {
            if (time > 0) {
                redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
            } else {
                set(key, value);
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }
    /**
     * 递增
     * @param key 键
     * @param delta 要增加几(大于0)
     * @return
     */
    public long incr(String key, long delta) {
        if (delta < 0) {
            throw new RuntimeException("递增因子必须大于0");
        }
        return redisTemplate.opsForValue().increment(key, delta);
    }

    /**
     * 递减
     * @param key 键
     * @param delta 要减少几(小于0)
     * @return
     */
    public long decr(String key, long delta) {
        if (delta < 0) {
            throw new RuntimeException("递减因子必须大于0");
        }
        return redisTemplate.opsForValue().increment(key, -delta);
    }

    // ================================Map=================================
    /**
     * HashGet
     * @param key 键 不能为null
     * @param item 项 不能为null
     * @return 值
     */
    public Object hget(String key, String item) {
        return redisTemplate.opsForHash().get(key, item);
    }

    /**
     * 获取hashKey对应的所有键值
     * @param key 键
     * @return 对应的多个键值
     */
    public Map<Object, Object> hmget(String key) {
        return redisTemplate.opsForHash().entries(key);
    }

    /**
     * HashSet
     * @param key 键
     * @param map 对应多个键值
     * @return true 成功 false 失败
     */
    public boolean hmset(String key, Map<String, Object> map) {
        try {
            redisTemplate.opsForHash().putAll(key, map);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }
    /**
     * HashSet 并设置时间
     * @param key 键
     * @param map 对应多个键值
     * @param time 时间(秒)
     * @return true成功 false失败
     */
    public boolean hmset(String key, Map<String, Object> map, long time) {
        try {
            redisTemplate.opsForHash().putAll(key, map);
            if (time > 0) {
                expire(key, time);
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 向一张hash表中放入数据,如果不存在将创建
     * @param key 键
     * @param item 项
     * @param value 值
     * @return true 成功 false失败
     */
    public boolean hset(String key, String item, Object value) {
        try {
            redisTemplate.opsForHash().put(key, item, value);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }
    /**
     * 向一张hash表中放入数据,如果不存在将创建
     * @param key 键
     * @param item 项
     * @param value 值
     * @param time 时间(秒) 注意:如果已存在的hash表有时间,这里将会替换原有的时间
     * @return true 成功 false失败
     */
    public boolean hset(String key, String item, Object value, long time) {
        try {
            redisTemplate.opsForHash().put(key, item, value);
            if (time > 0) {
                expire(key, time);
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 删除hash表中的值
     * @param key 键 不能为null
     * @param item 项 可以使多个 不能为null
     */

    public void hdel(String key, Object... item) {
        redisTemplate.opsForHash().delete(key, item);
    }

    /**
     * 判断hash表中是否有该项的值
     * @param key 键 不能为null
     * @param item 项 不能为null
     * @return true 存在 false不存在
     */
    public boolean hHasKey(String key, String item) {
        return redisTemplate.opsForHash().hasKey(key, item);
    }

    /**
     * hash递增 如果不存在,就会创建一个 并把新增后的值返回
     * @param key 键
     * @param item 项
     * @param by 要增加几(大于0)
     * @return
     */
    public double hincr(String key, String item, double by) {
        return redisTemplate.opsForHash().increment(key, item, by);
    }
    /**
     * hash递减
     * @param key 键
     * @param item 项
     * @param by 要减少记(小于0)
     * @return
     */
    public double hdecr(String key, String item, double by) {
        return redisTemplate.opsForHash().increment(key, item, -by);
    }
    // ============================set=============================
    /**
     * 根据key获取Set中的所有值
     * @param key 键
     * @return
     */
    public Set<Object> sGet(String key) {
        try {
            return redisTemplate.opsForSet().members(key);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 根据value从一个set中查询,是否存在
     * @param key 键
     * @param value 值
     * @return true 存在 false不存在
     */
    public boolean sHasKey(String key, Object value) {
        try {
            return redisTemplate.opsForSet().isMember(key, value);
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }
    /**
     * 将数据放入set缓存
     * @param key 键
     * @param values 值 可以是多个
     * @return 成功个数
     */
    public long sSet(String key, Object... values) {
        try {
            return redisTemplate.opsForSet().add(key, values);
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        }
    }

    /**
     * 将set数据放入缓存
     * @param key 键
     * @param time 时间(秒)
     * @param values 值 可以是多个
     * @return 成功个数
     */
    public long sSetAndTime(String key, long time, Object... values) {
        try {
            Long count = redisTemplate.opsForSet().add(key, values);
            if (time > 0)
                expire(key, time);
            return count;
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        }
    }

    /**
     * 获取set缓存的长度
     * @param key 键
     * @return
     */
    public long sGetSetSize(String key) {
        try {
            return redisTemplate.opsForSet().size(key);
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        }
    }

    /**
     * 移除值为value的
     * @param key 键
     * @param values 值 可以是多个
     * @return 移除的个数
     */
    public long setRemove(String key, Object... values) {
        try {
            Long count = redisTemplate.opsForSet().remove(key, values);
            return count;
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        }
    }
    // ===============================list=================================
    /**
     * 获取list缓存的内容
     * @param key 键
     * @param start 开始
     * @param end 结束 0 到 -1代表所有值
     * @return
     */
    public List<Object> lGet(String key, long start, long end) {
        try {
            return redisTemplate.opsForList().range(key, start, end);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 获取list缓存的长度
     * @param key 键
     * @return
     */
    public long lGetListSize(String key) {
        try {
            return redisTemplate.opsForList().size(key);
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        }
    }
    /**
     * 通过索引 获取list中的值
     * @param key 键
     * @param index 索引 index>=0时, 0 表头,1 第二个元素,依次类推;index<0时,-1,表尾,-2倒数第二个元素,依次类推
     * @return
     */
    public Object lGetIndex(String key, long index) {
        try {
            return redisTemplate.opsForList().index(key, index);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 将list放入缓存
     * @param key 键
     * @param value 值
     * @return
     */
    public boolean lSet(String key, Object value) {
        try {
            redisTemplate.opsForList().rightPush(key, value);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 将list放入缓存
     * @param key 键
     * @param value 值
     * @param time 时间(秒)
     * @return
     */
    public boolean lSet(String key, Object value, long time) {
        try {
            redisTemplate.opsForList().rightPush(key, value);
            if (time > 0)
                expire(key, time);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 将list放入缓存
     * @param key 键
     * @param value 值
     * @return
     */
    public boolean lSet(String key, List<Object> value) {
        try {
            redisTemplate.opsForList().rightPushAll(key, value);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 将list放入缓存
     *
     * @param key 键
     * @param value 值
     * @param time 时间(秒)
     * @return
     */
    public boolean lSet(String key, List<Object> value, long time) {
        try {
            redisTemplate.opsForList().rightPushAll(key, value);
            if (time > 0)
                expire(key, time);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 根据索引修改list中的某条数据
     * @param key 键
     * @param index 索引
     * @param value 值
     * @return
     */
    public boolean lUpdateIndex(String key, long index, Object value) {
        try {

            redisTemplate.opsForList().set(key, index, value);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }
    /**
     * 移除N个值为value
     * @param key 键
     * @param count 移除多少个
     * @param value 值
     * @return 移除的个数
     */
    public long lRemove(String key, long count, Object value) {
        try {
            Long remove = redisTemplate.opsForList().remove(key, count, value);
            return remove;
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        }
    }
}



4.最终导出效果

在这里插入图片描述

  • 3
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值