javascript
22-08-06 西安 尚医通(03)EasyExcel; Spring Cache 、Redis做缓存
EasyExcel
EasyExcel:一行一行讀取到內存
EasyExcel是阿里巴巴開源的一個excel處理框架,以使用簡單、節省內存著稱
POI:java里操作excel,讀取、創建excel
POI的缺點:耗內存。因為會把所有數據一起加載到內存中
EasyExcl讀寫演示
1.加依賴
<!-- https://mvnrepository.com/artifact/com.alibaba/easyexcel --><dependency><groupId>com.alibaba</groupId><artifactId>easyexcel</artifactId><version>2.1.1</version></dependency>2.@ExcelProperty,創建實體類Anchor用來和表格做映射
index第幾列,value列名
演示創建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);}效果如下:
—————————————————————————————————————————
演示讀取excel表格
EasyExcel采用一行一行的解析模式,
并將一行的解析結果以觀察者的模式通知處理AnalysisEventListener
invoke方法用于處理每條數據,讀者可以在這邊進行業務邏輯處理
doAfterAllAnalysed方法是只處理完所有數據后進行的動作;
主函數:正真讀取是按照第二行開始的
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();}}前端代碼
在前臺頁面點擊導出,
相關前端代碼大致如下:
不需要寫api部分,直接訪問。也可以訪問9001
exportData() {window.open("http://localhost:8202/admin/cmn/dict/exportData");},導出效果:
數據字典導入
后端接口
導入的excel文件格式,要滿足格式DictEeVo
Controller方法想接收一個文件,這里文件名稱必須是file,可以使用@RequestParam(“file”)
1、創建監聽器讀取
invoke,每讀一行,這個方法就調用一次
@Component public class DictListener extends AnalysisEventListener<DictEeVo> {@Autowiredprivate DictMapper dictMapper;//一行一行讀取@Overridepublic void invoke(DictEeVo dictEeVo, AnalysisContext analysisContext) {//調用方法添加數據庫Dict dict = new Dict();BeanUtils.copyProperties(dictEeVo,dict);dictMapper.insert(dict);}@Overridepublic void doAfterAllAnalysed(AnalysisContext analysisContext) {} }2.service層實現業務邏輯
@Overridepublic void importDictData(MultipartFile file) {try {EasyExcel.read(file.getInputStream(), DictEeVo.class, dictListener).sheet().doRead();} catch (IOException e) {e.printStackTrace();}}3.使用postman測試文件上傳接口
查看數據庫的字典表,導入成功
前端部分
點擊彈窗框上選擇導入的文件,visible.sync控制顯示隱藏彈出框
效果如下:
導入成功回調方法
Spring Cache
Spring Cache 是一個非常優秀的緩存組件;是Spring基于aop提供的自動緩存管理
只需要通過注解標注到查詢的業務方法上 可以將查詢方法返回的結果緩存起來,以后查詢時有緩存不在執行業務代碼
Spring Cache步驟:
1、為springcache提供一個緩存管理接口的實現
2、啟動類/配置類 添加**@EnableCaching注解
3、在需要緩存管理的業務方法(查詢)上使用@Cachable(…key )** 標注
spring會自動管理業務方法的數據緩存 調用第一步實現的緩存管理對象的生命周期方法管理緩存
1、緩存管理接口的實現
redis緩存:緩存的目的避免(減少)客戶端從mysql中讀取數據
1.添加依賴
<!-- redis --> <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId> </dependency><!-- spring2.X集成redis所需common-pool2--> <dependency><groupId>org.apache.commons</groupId><artifactId>commons-pool2</artifactId><version>2.6.0</version> </dependency>2.創建配置類(拷貝)
@EnableCaching 必須要加,否則spring-data-cache相關注解不會生效…
當然還有第二版,這一版是之后加的(這版是老師講過的,還算是能看懂。。。)
@Configuration public class RedisCacheConfig {//1、RedisTemplate配置鍵和值的序列化器@AutowiredRedisTemplate redisTemplate;@PostConstructpublic void init(){redisTemplate.setKeySerializer(new StringRedisSerializer());redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());}//2、緩存管理CacheManager接口的實現//LettuceConnectionFactory commons-pool2包中提供的Redis連接池工廠類@Beanpublic CacheManager cacheManager(LettuceConnectionFactory connectionFactory){RedisCacheConfiguration cacheConfig = RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofMinutes(30)) //緩存過期時間.disableCachingNullValues() //不緩存空值.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer())) //鍵序列化器.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()));//值序列化器RedisCacheManager cacheManager = RedisCacheManager.builder(connectionFactory).cacheDefaults(cacheConfig).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=02、使用@Cachable緩存管理
@Cacheable添加緩存
1、查詢數據字典+@Cacheable
- 如果緩存存在,則直接讀取緩存數據返回;
- 如果緩存不存在,則執行方法,并把返回的結果存入緩存中。
1.key 可選屬性,可以使用 SpEL 標簽自定義緩存的key, key中可以獲取業務方法的形參值
2.#號代表這是一個 SpEL 表達式,此表達式可以遍歷方法的參數對象
@Cacheable(value = "dictCache", key = "'dict_'+#id")
3. 緩存的key: 使用value和key的值拼接,如: dictCache::dict_id的值
如果參數是對象Dict dict,可以這么獲取
> key="'dict_'+#dict.value"
redis中效果:
@CacheEvict緩存驅逐
放在刪除、更新方法上
使用該注解**@CacheEvict**標志的方法,會清空指定的緩存。一般用在更新或者刪除方法上
allEntries 是否清除當前 value值空間下的所有緩存數據。默認false
導入數據的時候,把redis中數據都清除
@CacheEvict(value = "dictCache", allEntries = true)@Overridepublic void importDictData(MultipartFile file) {try {EasyExcel.read(file.getInputStream(), DictEeVo.class, dictListener).sheet().doRead();} catch (IOException e) {e.printStackTrace();}}@CachePut更新緩存
用在修改方法或添加方法上
同步緩存的做法
當mysql中的數據發生改變,就會清空redis中對應命名空間下的緩存數據
3、手動緩存管理
由于緩存雪崩的問題,我們需要手動管理緩存。。使TTL設置的時間不那么集中。雖然這是另外一個方法,不要在意這些細節。。。
@Autowired PmsClient pmsClient;//遠程服務調用的feign接口@Autowired RedisTemplate redisTemplate; //刪除上面的Cacheable注解 @Override public List<CategoryEntity> levelTwoAndSubsCates(String cid) {//使用緩存管理//1、從緩存中查詢cid的二級三級分類集合 如果有直接返回String key = "idx:cache:cates:"+cid;Object obj = redisTemplate.opsForValue().get(key);if(obj!=null){return (List<CategoryEntity>) obj;}//2、如果緩存沒有 再遠程查詢二三級分類集合ResponseVo<List<CategoryEntity>> responseVo = pmsClient.lv2AndSubsCates(cid);//將查詢結果存到緩存中redisTemplate.opsForValue().set(key,responseVo.getData() ,1800+new Random().nextInt(200), TimeUnit.SECONDS);return responseVo.getData(); }4、緩存一致性
數據庫數據更新后如何保證緩存的數據和數據庫數據一致:
-
雙寫模式:數據庫更新同時更新redis緩存數據
? 先寫哪一個存在問題:redis和數據庫之間的事務不容易保證
-
失效模式: 更新數據庫時 讓緩存失效
? 更新數據庫業務執行時 緩存會失效,此時如果數據還未寫成功 有請求查詢數據查到了數據庫中還未更新的數據到緩存中,此時才寫成功 緩存的數據和數據庫仍然不一致
-
雙刪模式:更新數據庫前刪除一次緩存 更新成功后再刪除一次緩存
-
數據庫同步中間件:基于mysql的binlog日志將數據庫更新的數據同步到第三方的中間件(redis mq es)
- canal:阿里開源的一個框架
5、緩存并發問題
1.緩存雪崩
緩存雪崩:首頁數據訪問量大,數據緩存過期時間接近 會導致多個緩存同時失效,大量的請求可能直接查詢數據庫
解決:隨機因子 讓多個熱門key失效的時間分散
2.緩存擊穿
緩存擊穿:單個熱點key突然失效,導致大量的請求同時訪問數據庫查詢數據
?解決:控制只讓一個線程查數據 其他的等待使用緩存 分布式鎖
3.緩存穿透
緩存穿透 : 訪問數據庫一定不存在的數據時,請求每次都會先查詢緩存,然后再查數據庫
解決:
總結
以上是生活随笔為你收集整理的22-08-06 西安 尚医通(03)EasyExcel; Spring Cache 、Redis做缓存的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 普及医疗类人工智能机器人的重要性
- 下一篇: gradle idea java ssm