首页 > 技术文章 > 自己挖的坑自己填--开发问题记录

huangrenhui 2020-05-15 10:37 原文

  1. StringButils.isBlank/isNotBlank:
    if(param==null||"".equals(param.trim()){....}
    if(!(param==null||"".equals(param.trim())){....}
    //使用isBlank/isNotBlank代替上面代码,更有可读性
    if (StringUtils.isBlank(chargeSchemeNew)) {....}
    if (StringUtils.isNotBlank(chargeSchemeNew)) {....}
  2. excel日期读取问题:使用Apache POI对excel数据进行导入处理时,在对日期数据的处理中,excel里面的日期格式是yyyy-MM-dd,比如“2020/9/30”,但poi处理为String类型后显示为“30-九月-2020”
    String  str ="30-九月-2020";
    Date date = new SimpleDateFormat("ddd-MMM-yyyy").parse(str);
    System.out.println(date);   
    //====结果====
    Wed Sep 30 00:00:00 CST 2020
    
    //====或直接获取excel数据再进行是否是日期的判断和处理
    public static String importByExcelForDate(Cell currentCell) {
        String currentCellValue = "";
        // 判断单元格数据是否是日期
        if ("yyyy/mm;@".equals(currentCell.getCellStyle().getDataFormatString())
                || "m/d/yy".equals(currentCell.getCellStyle().getDataFormatString())
                || "yy/m/d".equals(currentCell.getCellStyle().getDataFormatString())
                || "mm/dd/yy".equals(currentCell.getCellStyle().getDataFormatString())
                || "dd-mmm-yy".equals(currentCell.getCellStyle().getDataFormatString())
                || "yyyy/m/d".equals(currentCell.getCellStyle().getDataFormatString())) {
            if (DateUtil.isCellDateFormatted(currentCell)) {
                // 用于转化为日期格式
                Date d = currentCell.getDateCellValue();
                DateFormat formater = new SimpleDateFormat("yyyy-MM-dd");
                currentCellValue = formater.format(d);
            }
        } else {
            // 不是日期原值返回
            currentCellValue = currentCell.toString();
        }
        return currentCellValue;
    }
  3. excel数值读取问题:在对excel数据是数值比如“50"进行获取然后toString后,转换为"50.0",开始是进行String.substring(0,cellData.length()-2)截取,后面添加currentRow.getCell(j).setCellType(HSSFCell.CELL_TYPE_STRING)就可以把数据直接处理成"50"
    public static Map<String,List> analyzeExcel(MultipartFile file, Integer sheetIndex, Integer startRow) {
        List<AppointmentListVo> dataList = new LinkedList<>();
        Workbook wb = ExcelUtil.createWorkBook(file);
        if (wb != null) {
            Sheet sheet = wb.getSheetAt(sheetIndex);
            //一共有多少行
            int maxRownum = sheet.getPhysicalNumberOfRows();
            Row currentRow = sheet.getRow(startRow);
            //一共有多少列
            int maxColnum = currentRow.getPhysicalNumberOfCells();
            for (int i = startRow; i < maxRownum ; i ++) {
                currentRow = sheet.getRow(i);
                AppointmentListVo vo=new AppointmentListVo();
                if (currentRow != null) {
                    String cellData;
                    for (int j = 0; j < maxColnum; j++) {
                        //
                        currentRow.getCell(j).setCellType(HSSFCell.CELL_TYPE_STRING);
                        cellData = importByExcelForDate(currentRow.getCell(j));
                        if (j==0){
                            vo.setShortFirmName(cellData);
                        }else if(j==1){
                            vo.setTrainClassName(cellData);
                        } else if (j==2){
                            vo.setMacTypeName(cellData);
                        } else if (j==3){
                            vo.setDisplayChs(cellData);
                        }else if (j==4){
                            vo.setBeginTime(cellData);
                        }else if (j==5){
                            vo.setEndTime(cellData);
                        }else if(j==6){
                            //cellData =cellData.substring(0,cellData.length()-2);
                            vo.setTotalTrainPersons(Integer.parseInt(cellData));
                        }
                    }
                    dataList.add(vo);
                } else {
                    break;
                }
            }
        }
        Map<String,List> map = new HashMap<>();
        map.put("dataList",dataList);
        return map;
    }
  4. Dubbo消费者生产者先后启动问题:如果消费者模块先启动再启动生产者模块,会导致服务调用报空指针异常
    //解决消费者先启动,生产者后启动导致消费者为null的问题
    @Configuration
    public class DubboConfig {
        /**
         * 消费者配置不主动监督zookeeper服务
         *
         * @return
         */
        @Bean
        public ConsumerConfig consumerConfig() {
            ConsumerConfig consumerConfig = new ConsumerConfig();
            consumerConfig.setCheck(false);
            consumerConfig.setTimeout(40000);
            return consumerConfig;
        }
    } 
  5. MyBatis的xml里面写SQL,对”<“要使用”&lt;“代替:
      <select id="selectIdByCondition" resultType="string">
        SELECT id FROM tab WHERE
        begin_date &lt;= #{beginTimeDate} //等价小于等于<=
      </select>
  6. Oracle日期格式转换:比如将 ”2021-03-10“转换为 “10/Mar/2021”
    select initcap(to_char('2021-03-10','dd/mon/yyyy','NLS_DATE_LANGUAGE=American') from dual;
  7. Java虚拟机启动参数时区配置问题

    Java虚拟机在启动时如果未指定时区参数则可能使用的是非东八区时区,在Java程序中此时使用默认的SimpleDateFormat去格式化直接创建的Date对象时,返回的日期因为与系统时间存在时区差则可能会返回与系统日期相差一天的日期,如果此时直接使用此程序中获取的日期做其他操作则会出现当天做的操作被记为前一日或后一日的问题。

    解决方法:在JVM启动时添加启动参数 -Dusr.timezone=GMT+08:00 用于指定时区,以确保在程序中取得的日期与系统日期一致。

  8. ThreadLocal使用不当引发内存泄漏问题

    ThreadLocal可以保存线程级别的变量,相当于是每个线程中创建一个副本,内部是按 key-value 键值对保存数据的,key 是 ThreadLocal 对象的弱引用,value 是强引用方式;ThreadLocal 的引用如果被回收,key 就会为 null,调用 ThreadLocal 的 get/set/remove 方法会清理 key 为 null 的键值对,但是如果这三个方法都没调用过,则会导致强引用对象无法被回收,最终导致内存泄漏。所以在使用完之后要显式的 remove 掉。

    /**
     * 模拟ThreadLocal内存泄露导致OOM
     * JVM启动参数 -Xms20M -Xmx20M -Xmn10M
     */
    public static void main(String[] args) {
        // 是否调用remove方法
        boolean doRemove = false;
        // 加锁,让多个线程串行执行,避免多个线程同时占用内存导致的内存溢出问题
        Object lockObj = new Object();
        // 开启20个线程
        ExecutorService executorService = Executors.newFixedThreadPool(20);
        // 为了不重复使用线程,用Map标记一下已经已使用过的线程,
        Map<Long, Integer> threadIdMap = new ConcurrentHashMap<>();
        // 循环向线程变量中设置数据 1024 * 1024 = 1M
        for (int i = 0; i < 20; i++) {
            executorService.execute(() -> {
                synchronized (lockObj) {
                    Integer num = threadIdMap.putIfAbsent(Thread.currentThread().getId(), 1);
                    if (num == null) {
                        ThreadLocal<Byte[]> threadLocal = new ThreadLocal<>();
                        threadLocal.set(new Byte[1024 * 1024]);
                        if(doRemove) {
                            // 解决内存泄露关键
                            threadLocal.remove();
                        }
                        // 将threadLocal置为空引用,利于回收
                        threadLocal = null;
                        // 手工回收
                        System.gc();
                        try {
                            // 调用GC后不一定会马上回收
                            Thread.sleep(500);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
    
                    System.out.println(Thread.currentThread().getName());
                }
            });
        }
    }
  9. Java读取文件编码格式问题

    文件格式为GBK,JVM默认文件编码格式为UTF-8,读取文件后编码格式混乱,字符串长度与预期不一致,将读取的String转成GBK格式依然为乱码。

    原因:
    Java程序读取文件时,若未明确指定文件的编码格式,则使用JVM的默认文件编码格式读取文件内容,转换成 Unicode 编码。若读取的文件编码与JVM的默认文件编码格式不一致,读取到JVM中的数据已经编码错乱,无论后续将 String 转换成 GBK 还是 UTF-8 格式,都无法恢复正确编码。

    解决办法:
    读取文件时,指定BufferedReader的编码格式,以下是参考代码

        public static void main(String[] args) throws IOException {
           String tempString = null;
           File file = new File("Emp_plan_20210110.bin");
            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(new FileInputStream(file),"GB18030"));
            while((tempString = bufferedReader.readLine())!=null){
                byte[] bytes = tempString.getBytes("GB18030");
                System.out.println(bytes.length);
            }
        }
  10. jpa @Lob 存储大文本 org.postgresql.util.PSQLException: 大型对象无法被使用在自动确认事物交易模式

    解决办法:在文本字段的@Lob前添加 @org.hibernate.annotations.Type(type = "org.hibernate.type.TextType")

  11. jpa 在 postgresql 下不支持别名,比如 @Query(value = "update tableName tb set tb.ISACTIVATE= ?2  where tb.RID= ?1", nativeQuery = true) 中需将 tb 别名去掉;

推荐阅读