• 22-08-06 西安 EasyExcel实现字典表导入、导出


    EasyExcel

    EasyExcel:一行一行读取到内存
    EasyExcel是阿里巴巴开源的一个excel处理框架,以使用简单、节省内存著称

    POI:java里操作excel,读取、创建excel

    POI的缺点:耗内存。因为会把所有数据一起加载到内存中


    EasyExcl读写演示

    1.加依赖

            
            <dependency>
                <groupId>com.alibabagroupId>
                <artifactId>easyexcelartifactId>
                <version>2.1.1version>
            dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    2.@ExcelProperty,创建实体类Anchor用来和表格做映射
    index第几列,value列名

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public class Anchor {
        @ExcelProperty(index = 0,value = "主播姓名")
        private String name;
    
        @ExcelProperty(index = 1,value = "直播平台")
        private String platform;
    
        @ExcelProperty(index=2,value = "成名英雄")
        private String hero;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    演示创建excel表格

    一行代码,调用3个方法 EasyExcel.write().sheet().doWrite();

        public static void main(String[] args) {
            Anchor anchor1 = new Anchor("北慕", "虎牙", "露娜");
            Anchor anchor2 = new Anchor("骚白", "斗鱼", "花木兰");
            Anchor anchor3 = new Anchor("赖神", "虎牙", "老夫子");
            List<Anchor> anchorList = Arrays.asList(anchor1, anchor2, anchor3);
            EasyExcel.write("C:\\Users\\lenovo\\Desktop\\主播列表.xlsx",Anchor.class)
                    .sheet("主播列表")
                    .doWrite(anchorList);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    效果如下:
    在这里插入图片描述
    —————————————————————————————————————————

    演示读取excel表格

    EasyExcel采用一行一行的解析模式,
    并将一行的解析结果以观察者的模式通知处理AnalysisEventListener

    invoke方法用于处理每条数据,读者可以在这边进行业务逻辑处理
    doAfterAllAnalysed方法是只处理完所有数据后进行的动作;

    public class ExcelListener extends AnalysisEventListener<Anchor> {
    
        //一行一行去读取excle内容
        @Override
        public void invoke(Anchor anchor, AnalysisContext analysisContext) {
            System.out.println("***"+anchor);
        }
    
        //所有行读取完成后执行
        @Override
        public void doAfterAllAnalysed(AnalysisContext analysisContext) {
            System.out.println("读取完成后");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    主函数:正真读取是按照第二行开始的

        public static void main(String[] args) {
            String fileName = "C:\\Users\\lenovo\\Desktop\\主播列表.xlsx";
            // 这里 需要指定读用哪个class去读,然后读取第一个sheet 文件流会自动关闭
            EasyExcel.read(fileName, Anchor.class, new ExcelListener())
                    .sheet("主播列表")
                    .doRead();
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    控制台打印如下:
    在这里插入图片描述


    数据字典导出

    后端代码

    可以理解为文件下载功能

    DictEeVo,导出的数据字典格式

    @Data
    public class DictEeVo {
    
    	@ExcelProperty(value = "id" ,index = 0)
    	private Long id;
    
    	@ExcelProperty(value = "上级id" ,index = 1)
    	private Long parentId;
    
    	@ExcelProperty(value = "名称" ,index = 2)
    	private String name;
    
    	@ExcelProperty(value = "值" ,index = 3)
    	private String value;
    
    	@ExcelProperty(value = "编码" ,index = 4)
    	private String dictCode;
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    字典文件下载controller层,返回值为void就行,必须用response对象

    @ApiOperation(value="导出")
    @GetMapping(value = "/exportData")
    public void exportData(HttpServletResponse response) {
        dictService.exportData(response);
    } 
    
    • 1
    • 2
    • 3
    • 4
    • 5

    正真的实现是在service层。如下

        public void exportData(HttpServletResponse response) {
            try {
                //设置响应头
                response.setContentType("application/vnd.ms-excel"); //指示响应内容的格式
                response.setCharacterEncoding("utf-8");
    
                // 这里URLEncoder.encode可以防止中文乱码 当然和easyexcel没有关系
                String fileName = URLEncoder.encode("数据字典", "UTF-8");
                // 指示响应内容以附件形式下载
                response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".xlsx");
    
                //封装数据集合
                List<Dict> dictList = baseMapper.selectList(null);
                ArrayList<DictEeVo> dictEeVos = new ArrayList<>();
                dictList.forEach(dict -> {
                    DictEeVo dictEeVo = new DictEeVo();
                    BeanUtils.copyProperties(dict, dictEeVo);
                    dictEeVos.add(dictEeVo);
                });
                //用流的方式,浏览器文件下载
                EasyExcel.write(response.getOutputStream(), DictEeVo.class)
                        .sheet("数据字典")
                        .doWrite(dictEeVos);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    前端代码

    在前台页面点击导出,

    在这里插入图片描述
    相关前端代码大致如下:

          <div class="el-toolbar-body" style="justify-content: flex-start">
            <el-button type="primary" @click="exportData"
              ><i class="el-icon-download el-icon--right" /> 导出el-button
            >  ```
    
    • 1
    • 2
    • 3
    • 4

    不需要写api部分,直接访问。也可以访问9001

     exportData() {
          window.open("http://localhost:8202/admin/cmn/dict/exportData");
        },
    
    • 1
    • 2
    • 3

    导出效果:
    在这里插入图片描述


    数据字典导入

    后端接口

    导入的excel文件格式,要满足格式DictEeVo
    在这里插入图片描述
    Controller方法想接收一个文件,这里文件名称必须是file,可以使用@RequestParam(“file”)

        @ApiOperation(value = "导入")
        @PostMapping("importData")
        public R importData(MultipartFile file) {
            dictService.importDictData(file);
            return R.ok();
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    1、创建监听器读取

    invoke,每读一行,这个方法就调用一次

    @Component
    public class DictListener extends AnalysisEventListener<DictEeVo> {
    
        @Autowired
        private DictMapper dictMapper;
    
        //一行一行读取
        @Override
        public void invoke(DictEeVo dictEeVo, AnalysisContext analysisContext) {
            //调用方法添加数据库
            Dict dict = new Dict();
            BeanUtils.copyProperties(dictEeVo,dict);
            dictMapper.insert(dict);
        }
        @Override
        public void doAfterAllAnalysed(AnalysisContext analysisContext) {
    
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    2.service层实现业务逻辑
        @Override
        public void importDictData(MultipartFile file) {
            try {
                EasyExcel.read(file.getInputStream(), DictEeVo.class, dictListener)
                        .sheet()
                        .doRead();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    3.使用postman测试文件上传接口在这里插入图片描述

    查看数据库的字典表,导入成功
    在这里插入图片描述


    前端部分

    在这里插入图片描述
    点击弹窗框上选择导入的文件,visible.sync控制显示隐藏弹出框

        <el-dialog title="导入" :visible.sync="dialogImportVisible" width="480px">
          <el-form label-position="right" label-width="170px">
            <el-form-item label="文件">
              <el-upload
                :multiple="false"
                :on-success="onUploadSuccess"
                :action="'http://localhost:8202/admin/cmn/dict/importData'"
                class="upload-demo"
              >
                <el-button size="small" type="primary">点击上传el-button>
                <div slot="tip" class="el-upload__tip">
                  只能上传xls文件,且不超过500kb
                div>
              el-upload>
            el-form-item>
          el-form>
          <div slot="footer" class="dialog-footer">
            <el-button @click="dialogImportVisible = false">取消el-button>
          div>
        el-dialog>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    效果如下:
    在这里插入图片描述
    导入成功回调方法

        importData() {
          this.dialogImportVisible = true;
        },
        
        onUploadSuccess(response, file) {
          this.$message.info("导入成功");
          //关闭弹出框
          this.dialogImportVisible = false;
          //重新查询一级数据
          this.getDictList(1);
        },
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    Spring Cache

    Spring Cache 是一个非常优秀的缓存组件;

    自Spring 3.1起,提供了类似于@Transactional注解事务的注解Cache支持;

    缓存逻辑透明的应用到我们的业务代码上,且只需要更少的代码就可以完成;

    提供事务回滚时也自动回滚缓存;
    在这里插入图片描述

    redis

    Redis是当前比较热门的NOSQL系统之一,它是一个开源的使用ANSI c语言编写的key-value存储系统(区别于MySQL的二维表格的形式存储)
    nosql,内存存储,持久化

    官方数据:
    Redis特点:

    1. Redis读取的速度是110000次/s,写的速度是81000次/s;

    2. 每一个命令都是原子的

    3. redis事务,相对于mysql级别差很多

    4. redis可以实现类似消息队列的功能,支持发布订阅


    redis可以作为缓存服务器。session服务器
    在这里插入图片描述


    集成redis准备工作

    1.添加依赖

    
    <dependency>
         <groupId>org.springframework.bootgroupId>
         <artifactId>spring-boot-starter-data-redisartifactId>
    dependency>
    
    
    <dependency>
         <groupId>org.apache.commonsgroupId>
         <artifactId>commons-pool2artifactId>
         <version>2.6.0version>
    dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    2.创建配置类(拷贝)
    在这里插入图片描述
    @EnableCaching 必须要加,否则spring-data-cache相关注解不会生效…

    @Configuration
    @EnableCaching
    public class RedisConfig {
    
        /**
         * 设置RedisTemplate规则
         * @param redisConnectionFactory
         * @return
         */
        @Bean
        public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
            RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<>();
            redisTemplate.setConnectionFactory(redisConnectionFactory);
            Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
    
    //解决查询缓存转换异常的问题
            ObjectMapper om = new ObjectMapper();
    // 指定要序列化的域,field,get和set,以及修饰符范围,ANY是都有包括private和public
            om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
    // 指定序列化输入的类型,类必须是非final修饰的,final修饰的类,比如String,Integer等会跑出异常
            om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
            jackson2JsonRedisSerializer.setObjectMapper(om);
    
    //序列号key value
            redisTemplate.setKeySerializer(new StringRedisSerializer());
            redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
            redisTemplate.setHashKeySerializer(new StringRedisSerializer());
            redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);
    
            redisTemplate.afterPropertiesSet();
            return redisTemplate;
        }
    
        /**
         * 设置CacheManager缓存规则
         * @param factory
         * @return
         */
        @Bean
        public CacheManager cacheManager(RedisConnectionFactory factory) {
            RedisSerializer<String> redisSerializer = new StringRedisSerializer();
            Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
    
    //解决查询缓存转换异常的问题
            ObjectMapper om = new ObjectMapper();
            om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
            om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
            jackson2JsonRedisSerializer.setObjectMapper(om);
    
    // 配置序列化(解决乱码的问题),过期时间600秒
            RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
                    .entryTtl(Duration.ofSeconds(600))
                    .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer))
                    .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer))
                    .disableCachingNullValues();
    
            RedisCacheManager cacheManager = RedisCacheManager.builder(factory)
                    .cacheDefaults(config)
                    .build();
            return cacheManager;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62

    3.配置文件

    spring.redis.host=192.168.2.108
    spring.redis.port=6379
    spring.redis.database= 0
    spring.redis.timeout=1800000
    
    #连接池最大连接数
    spring.redis.lettuce.pool.max-active=20
    #连接池最大阻塞等待时间(使用负值表示没有限制)
    spring.redis.lettuce.pool.max-wait=-1
    #连接池中的最大空闲连接
    spring.redis.lettuce.pool.max-idle=5
    #连接池中的最小空闲连接
    spring.redis.lettuce.pool.min-idle=0
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    linux服务器上使用docker方式安装redis

    运行容器
    在这里插入图片描述


    集成redis演示

    1、查询数据字典

        @GetMapping("findChildData/{id}")
        @ApiOperation("根据id查询子数据列表")
        public R findChildData(@PathVariable Long id) {
            List<Dict> list = dictService.findChildData(id);
            return R.ok().data("list",list);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    @Cacheable 查询 添加缓存

    • 如果缓存存在,则直接读取缓存数据返回;

    • 如果缓存不存在,则执行方法,并把返回的结果存入缓存中。

    key 可选属性,可以使用 SpEL 标签自定义缓存的key
    #号代表这是一个 SpEL 表达式,此表达式可以遍历方法的参数对象

        //value:命名空间
        @Cacheable(value = "dictCache", key = "'dict_'+#id")
        @Override
        public List<Dict> findChildData(Long id) {
            QueryWrapper<Dict> queryWrapper = new QueryWrapper<>();
            queryWrapper.eq("parent_id", id);
            //向list集合每个dict对象中设置hasChildren
            List<Dict> dicts = baseMapper.selectList(queryWrapper);
            System.out.println("从mysql中查询id=" + id + "的数据");
            dicts.forEach(dict -> {
                dict.setHasChildren(this.isChildren(dict.getId()));
            });
            return dicts;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    如果参数是对象,可以这么获取
    在这里插入图片描述
    redis中效果:
    在这里插入图片描述


    @CacheEvict 放在删除、更新方法上 缓存驱逐

    使用该注解**@CacheEvict**标志的方法,会清空指定的缓存。一般用在更新或者删除方法上
    allEntries 是否清除当前value值空间下的所有缓存数据。默认false

    导入数据的时候,把redis中数据都清除

        @CacheEvict(value = "dictCache", allEntries = true)
        @Override
        public void importDictData(MultipartFile file) {
            try {
                EasyExcel.read(file.getInputStream(), DictEeVo.class, dictListener)
                        .sheet()
                        .doRead();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    @CachePut用在修改方法或添加方法上,更新缓存
    在这里插入图片描述

    同步缓存的做法
    当mysql中的数据发生改变,就会清空redis中对应命名空间下的缓存数据
    在这里插入图片描述


  • 相关阅读:
    计算机网络-网络层 (IPV6,IPV4与IPV6对比,IPV6地址类型)
    基于前馈式模糊控制的公路隧道通风系统研究
    VMware Horizon 8 运维系列(二)win10设置共享桌面图标
    Masa Blazor in Blazor Day
    【nginx代理postgresql】
    Assigning a Static IP Address to a WSL2 Distribution
    每日五问(java)
    【深入浅出Nginx系列】Nginx入门?看这一篇就够了(概念篇)
    J1_搭建Web环境,初识JSP
    NTM中attr的用法
  • 原文地址:https://blog.csdn.net/m0_56799642/article/details/126206518