EasyExcel:一行一行读取到内存
EasyExcel是阿里巴巴开源的一个excel处理框架,以使用简单、节省内存著称
POI:java里操作excel,读取、创建excel
POI的缺点:耗内存。因为会把所有数据一起加载到内存中
1.加依赖
<dependency>
<groupId>com.alibabagroupId>
<artifactId>easyexcelartifactId>
<version>2.1.1version>
dependency>
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;
}
一行代码,调用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);
}
效果如下:

—————————————————————————————————————————
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("读取完成后");
}
}
主函数:正真读取是按照第二行开始的
public static void main(String[] args) {
String fileName = "C:\\Users\\lenovo\\Desktop\\主播列表.xlsx";
// 这里 需要指定读用哪个class去读,然后读取第一个sheet 文件流会自动关闭
EasyExcel.read(fileName, Anchor.class, new ExcelListener())
.sheet("主播列表")
.doRead();
}
控制台打印如下:

可以理解为文件下载功能
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;
}
字典文件下载controller层,返回值为void就行,必须用response对象
@ApiOperation(value="导出")
@GetMapping(value = "/exportData")
public void exportData(HttpServletResponse response) {
dictService.exportData(response);
}
正真的实现是在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();
}
}
在前台页面点击导出,
相关前端代码大致如下:<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");
},
导出效果:

导入的excel文件格式,要满足格式DictEeVo

Controller方法想接收一个文件,这里文件名称必须是file,可以使用@RequestParam(“file”)
@ApiOperation(value = "导入")
@PostMapping("importData")
public R importData(MultipartFile file) {
dictService.importDictData(file);
return R.ok();
}
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) {
}
}
@Override
public void importDictData(MultipartFile file) {
try {
EasyExcel.read(file.getInputStream(), DictEeVo.class, dictListener)
.sheet()
.doRead();
} catch (IOException e) {
e.printStackTrace();
}
}

查看数据库的字典表,导入成功


点击弹窗框上选择导入的文件,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>
效果如下:

导入成功回调方法
importData() {
this.dialogImportVisible = true;
},
onUploadSuccess(response, file) {
this.$message.info("导入成功");
//关闭弹出框
this.dialogImportVisible = false;
//重新查询一级数据
this.getDictList(1);
},
Spring Cache 是一个非常优秀的缓存组件;
自Spring 3.1起,提供了类似于@Transactional注解事务的注解Cache支持;
缓存逻辑透明的应用到我们的业务代码上,且只需要更少的代码就可以完成;
提供事务回滚时也自动回滚缓存;

Redis是当前比较热门的NOSQL系统之一,它是一个开源的使用ANSI c语言编写的key-value存储系统(区别于MySQL的二维表格的形式存储)
nosql,内存存储,持久化
官方数据:
Redis特点:
Redis读取的速度是110000次/s,写的速度是81000次/s;
每一个命令都是原子的
redis事务,相对于mysql级别差很多
redis可以实现类似消息队列的功能,支持发布订阅
redis可以作为缓存服务器。session服务器

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>
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;
}
}
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
linux服务器上使用docker方式安装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);
}
@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;
}
如果参数是对象,可以这么获取
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();
}
}
@CachePut用在修改方法或添加方法上,更新缓存

同步缓存的做法
当mysql中的数据发生改变,就会清空redis中对应命名空间下的缓存数据