当前位置: 首页 > news >正文

实习工作难点记录

前言

不知不觉已经实习两个月了,记录一下 ‘美好’ 的实习生活



难点1. 在已有的excel表内追加数据不发生栈溢出(10w条数据,关键字:‘已有’,‘追加’)

之前没接触过excel,一上手就来个有意思的(公司用的poi)

在网上找了半圈只找到,读取大数据量的api(Stream流)和往空文件写入大量数据的api(SXSSFWorkbook)

他们俩的共同特点都是将内存里的数据用一段扔一段,于是用了一个很笨的办法实现了需求。

大概思路就是用Stream流读取数据,中间再对数据进行判断,如果符合新增数据的条件,就把要增加的数据添加进读取的数据中,最后再用 SXSSFWorkbook 向空文件写入。

缺点就是效率低,excel的样式丢失(读取的样式有问题)

贴一下代码

 /*** 根据map 对大批量数据的EXCEL 进行写操作* @param map* @param file* @param sheetIndex*/public static void inserLargeExcelByMap(Map<String, Object> map, File file, int sheetIndex) {//输出流BufferedOutputStream bos = null;try (FileInputStream fis = new FileInputStream(file);//原excelWorkbook workbook = StreamingReader.builder().rowCacheSize(100).bufferSize(4096).open(fis);//新excelSXSSFWorkbook sxssfWorkbook = new SXSSFWorkbook();) {//1.获取 sheetSheet oldSheet = workbook.getSheetAt(sheetIndex);SXSSFSheet newSheet = sxssfWorkbook.createSheet(oldSheet.getSheetName());//用于标记新增数据是否插入boolean flag = false;for (String key : map.keySet()) {long columnIndex = transLetter2Num(key.replaceAll("[0-9]", ""));long rowIndex = Long.parseLong(key.replaceAll("[a-zA-Z]", ""));//2.1遍历原excel 填充数据到新 excelfor (Row oldRow : oldSheet) {//2.2 创建新rowSXSSFRow newRow = newSheet.createRow(oldRow.getRowNum());//2.3 判断插入数据的行 是否在所在行if (rowIndex == oldRow.getRowNum()) {for (Cell oldCell : oldRow) {if (columnIndex == oldCell.getColumnIndex()) {throw new RuntimeException("要插入数据的行列在excel已存在");} else {//2.4 填充原数据到新excelnewRow.createCell(oldCell.getColumnIndex()).setCellValue(oldCell.getStringCellValue());}}//2.5 插入新增数据到新excelnewRow.createCell((int) columnIndex).setCellValue(map.get(key).toString());//2.6 设置插入标记flag = true;} else {// 3.1 新数据的行不在原excel 已有行时for (Cell oldCell : oldRow) {//3.2 填充原数据到新excelnewRow.createCell(oldCell.getColumnIndex()).setCellValue(oldCell.getStringCellValue());}}}//4 遍历完所有数据,新数据还没插入时,插入新数据if (!flag) {newSheet.createRow((int) rowIndex).createCell((int) columnIndex).setCellValue(map.get(key).toString());}}//遍历完之后 将原excel删除String filePath = file.getAbsolutePath();file.delete();bos  = new BufferedOutputStream(new FileOutputStream(filePath));sxssfWorkbook.write(bos);sxssfWorkbook.dispose();} catch (FileNotFoundException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();} finally {if (bos != null) {try {bos.close();} catch (IOException e) {e.printStackTrace();}}}}/*** 将 excel 中的字母列索引转换为数字列索引** @param letterColIdx 字母列索引* @return rlt 返回字母列索引对应的数字索引 A -> 0  BA -> 52 CA -> 78* */public static long transLetter2Num(String letterColIdx) {long rlt = 0;letterColIdx = letterColIdx.toUpperCase();String[] sts = letterColIdx.split("");int length = sts.length;for (int i = 0; i < length; i++) {String st = sts[i];char[] cs = st.toCharArray();int idx = (int)cs[0];// 如果字符不是 A-Z 之间的字符, 则抛错if (idx < 65 || idx > 90) {throw new ExcelAnalysisException("Characters["+ st +"] not between A and Z.");}idx -= 64;long num = (long) (idx * Math.pow(26, length - i - 1));rlt  += num;}return --rlt;}



难点2. 优化某个代码片段的执行速度

拿到代码跑了一下,好家伙,跑了半个小时抛出死锁异常(人生第一次遇到死锁…),仔细看了看代码逻辑,发现以下几个问题。


问题一: 有一条查询所有数据的sql大致是
select * from table where column = '某1' order by '某2'

查看设计表,只有一个 id 主键索引,这条sql大概运行了接近10s(公司电脑比较烂),查出来10w条多点的数据

然后我把 sql 改为下面的语句,用时4s,sql优化知识之前几乎是空白,只在B站上看过一次图灵学院的免费公开课。

select * from table a, (select id from table where column = '某1') b where a.id = b.id order by '某2'

最后采用的是这条语句,并给 ‘某一,某二’增加了联合索引,用时1.9s

select 需要的字段 from table where column = '某1' order by '某2'

本来想试试这条语句,结果报错了,没搞懂为啥

select 需要的字段 from table a, (select id from table where column = '某1') b where a.id = b.id order by '某2'

问题二: 代码中有一个逻辑是将上述sql查询的10w条数据遍历,并根据条件进行更新,每一次循环更新的语句大概有8条…遍历完对数据库进行的IO操作至少以万为单位,这代码能运行快那才见鬼了

解决思路:想起来Mybatis-Plus中有条批量删除的语句,于是百度搜索关键字 mysql 批量更新
找到这条语句

UPDATE mytable SETmyfield = CASE idWHEN 1 THEN 'value'WHEN 2 THEN 'value'WHEN 3 THEN 'value'END
WHERE id IN (1,2,3)

接下来就好办了,再循环的时候 加上拼接 sql 字符串的逻辑,最后跑代码只用了 10s不到的样子



难点3. 生成 PDF 报销信息

一开始是打算先手动生成一个pdf模板然后用 pdfbox 替换关键字,结果遇到了中文乱码的问题,试了好几个编码都不行,进官网只找到 api 文档 (弱鸡看不懂),最后百度找到了一个很好用的 jar 包 Spire文档地址

用段落➕表格 生成了 需要的PDF

过程中遇到的问题:

  1. 中文乱码,需要使用支持中文的字体
  2. 使用了宋体但是 linux环境 好像没有这个字体,导致报错,于是使用了从网上下载了免费商用的字体



难点4. 将 2.5w 个 word合同关键信息 提取,并添加进excel进行汇总

这一下 Office 三件套一样没落下,难点在于各个 word 里面的信息有差异,关键字也不一样,目前没找到啥好的解决办法



难点5. 会议室预约的前端实现

技术栈是vue + ant deign of vue,这算是我真正意义上使用 vue 开发项目,之前在学校有开发过一款跑腿小程序(行云跑腿,服务器到期了…),算是写着练手玩的。

需求:

  1. 响应式布局,支持 手机 和 PC
  2. 外观样式,自己看着办…

之前开发的小程序就是仿的 UU跑腿界面,有经验… ,最后决定模仿挺好看的一个日历界面

接手的当天晚上就回家恶补 vue vuex vue router的知识(之前有学过,但是很久没用忘的差不多了)

难点:

  1. 根据会议时常,鼠标移动时,对应时间段的div背景色改变。解决思路:给每个div加上 开始时间和结束时间的属性
  2. 切换会议室,如何刷新页面并且保留 data 数据。解决思路:将关键数据和 父组件同步,切换会议室时,通知父组件 配合 v-if 销毁重建 子组建
  3. 切换会议室,点击上一页,下一页增加过渡动画,模仿的那个日历做的动画很自然,我做的动画幅度太大,之前一直想通过修改动画间隔粒度来让动画变得自然。最后通过修改 移动的距离,完成了动画的复刻,模仿的挺像~

后端主要就是同时抢占会议室锁的问题,由于只是公司的几个领导会使用这个功能,并且代码应该是部署到一台服务器的,所以就使用了 synchronized 代码块

如果部署到多台服务器,可以用redis 实现一个简单的分布式锁
思路:通过 redis 的原子性操作,给 redis增加键值对。

  1. 只有当键不存在时,才增加
  2. 为防止应用宕机,需设置过期时间,自动将锁归还
  3. 为防止应用任务堵塞后(超过key过期时间),归还锁时,将别的任务加的锁给删除,解决办法可以再开一个线程定时重置锁的过期时间。



分享一个优雅的参数校验方法:validation

maven坐标:

        <!--参数检验--><dependency><groupId>org.hibernate.validator</groupId><artifactId>hibernate-validator</artifactId></dependency>

简单示例:

最后加一个异常拦截配置

具体 API 参考



记录一次奇怪的BUG

bug问题:提交数据时,数据库插入了两条一样的数据

debug调试时,发现运行一条sql语句的代码,打印出一堆sql日志,一开始以为是通用mapper的问题,
后来发现是前端循环发送了四条异步请求,造成的并发问题。

加锁后发现问题依然存在,看了很久的代码,觉得逻辑没问题(第一次请求新增,后三次请求更新)
最后发现@Transactional注解 会使 synchronized关键字失效 ( synchronized代码块里执行的内容是在事务里面,在事务commit前可能有多个线程进入代码块),@Transactional注解又加在了整个service类上,给controller加锁,又会导致锁的粒度太大,最后交给了一个的前端实习妹子,让她用promise发同步请求



总结

  1. 由于大部分的任务都是对老项目(OA项目)进行维护和升级,深刻的明白了项目分层的意义,老项目没有service层
    一个Controller 快一万行代码,每个方法都看的头皮发麻
  2. 变量命名要规范!! 老项目前端代码甚至出现 a1,a2,a3 … a21 这样的神奇变量命名
  3. 参数校验!! 打开项目 总是点这点着就报错,点开控制台 熟悉的 java.lang.NullPointerException,不知道使用的员工是怎么受得了的…

http://www.taodudu.cc/news/show-1476390.html

相关文章:

  • web安全day03
  • aws s3
  • 如何使用AzurEnum快速枚举Microsoft Entra ID(Azure AD)
  • 开发中如何选择结合实现类
  • 设计模式-动态代理
  • Java基础篇常见面试问题总结
  • c 结构体之位域(位段)
  • 辗转相除求最大公约数,最大公倍数
  • Ubuntu“无法解析或打开软件包的列表或是状态文件”的解决办法。
  • 错误:cc1: error: unrecognized command line option “-m32”
  • 在编写mini2440 helloworld驱动遇到的问题
  • [leetcode] Median of Two Sorted Arrays 寻找两个有序数组的中位数
  • [leetcode] Reverse Integer 反转一个整数
  • [leetcode] Palindrome Number 回文数判断
  • [leetcode] Longest Common Prefix 字符窜最长公共前缀判断
  • [leetcode] Single Number 查找数组中的单数
  • [leetcode] Power of Two 判断一个数是否是2的平方
  • [leetcode] Max Points on a Line 判断最多有多少个点在同一条直线上
  • 使用selenium webdriver进行元素定位
  • 一个动态增长的栈实现
  • sublime cscope使用方法
  • [leetcode] 24. Swap Nodes in Pairs
  • sublime text常用快捷键整理
  • kmp算法字符串匹配C语言实现
  • VLC设置串流的TTL值
  • [leetcode]328. Odd Even Linked List
  • [leetcode]326. Power of Three(c语言)
  • [leetcode ]221. Maximal Square c语言
  • [leetcode]84. Largest Rectangle in Histogram c语言
  • [leetcode]36. Valid Sudoku c语言
  • python 中 print 函数用法总结
  • python 装饰器粗浅理解
  • ubuntu 15.04安装VMware11
  • ubuntu15.04在安装完vmware11后打开提示 VMware Kernel Module Updater
  • 安装win10和ubuntu双系统启动不了解决
  • make: 'pcs.o' is up to date.问题解决