从今天开始在这纪录我编程过程当中所遇到的坑,这些坑是彻底能够避免的,分享出来但愿你们不要再入坑,你们也能够分享出踩过的坑sql
一、生成文件的坑(1) |
编程过程当中生成文件是一个很常见的需求,为了图方便我使用了反射去获取全部的字段,再一次写入文件中,我觉得这是个很巧的方式,结果后来发现BUG了,花了很长的时间去找到这个根源所在,话很少说先贴出个人代码数据库
protected static String getLineString(Object obj) throws IllegalAccessException, IOException{ StringBuilder sb = new StringBuilder(); Field[] fields = obj.getClass().getDeclaredFields(); boolean access = false; for(int i = 1; i < fields.length; i++){ access = fields[i].isAccessible(); if(!access){ fields[i].setAccessible(true); } if(fields[i].get(obj) == null || fields[i].get(obj).equals("null")){//null不显示 }else { sb.append(String.valueOf(fields[i].get(obj))); } if(i != fields.length -1) {//最后一行不加 sb.append(SEPARATOR); } if(!access){ fields[i].setAccessible(false); } } return sb.toString(); }
后来我去读文件的时候发现读一个字段一直读得都不对,这可害苦了我,后来我才想起来是否是我生成文件的地方有问题,把实体类和数据库字段顺序一比对,果真,其中有几个字段和数据库字段的顺序不同。下次千万不能由于省事去用反射了,仍是一个个字段的写吧,并且这样也有好处,就是后来加字段和改字段了都不会受到影响,否则另一个同事某天修改了这块你不知道,那问题可就大了。编程
二、生成文件的坑(2) |
在写文件的时候每每会有一些特殊的处理,好比往第一行插入统计的数据,这时候就要用到RandomAccessFile类的相关方法,然而这里面也是有不少坑的,先看下面这段代码。app
protected void writeFirstLine(String firstLine, String filePath){ RandomAccessFile raf = null; try { raf = new RandomAccessFile(filePath, "rw"); raf.seek(0); raf.write(firstLine.getBytes("UTF-8")); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); }finally { try { raf.close(); } catch (IOException e) { e.printStackTrace(); } } }
在第一行的起始位置是插入了咱们想要的数据了,然而其它的数据就会被覆盖,这时候如何办是好呢,将整个文件读取出来而后在前面插入第一行数据,而后再写入文件?遇到特别大的文件的时候这种方法就GG了。如是我也只能在建立文件的时候先在文件里写入一行空格,而后后面再调用上面的方法。dom
/** * 写入本地临时文件第一行占位符 * @param file */ protected void writeTempFirstLine(File file){ try { FileUtils.writeStringToFile(file," ".concat(IOUtils.LINE_SEPARATOR)); } catch (IOException e) { e.printStackTrace(); logger.error(">>>>>>>>>本地临时文件第一行占位符号数据写入异常", e); } }
方法着实的有点low啊,不过仍是能解决这个问题的,若是第一行临时占位的数据有其它需求能够重写上面的方法,你们有更好的方法也但愿能提出来。ui
三、分页查询的坑 |
在实际的开发过程当中会常常用到分页查询,尤为是数据量大的状况下,根据起始ID加上pageSize;而后用一个while(true){}的循环去处理中间的逻辑,这个中会用到break,continue以及return,稍不注意就会遇到相似于死循环等一些逻辑问题。那么咱们必定要注意哪些点呢?
一、当咱们在循环中用到return的时候必定要问下本身:后面的处理步骤是否依赖循环处理的数据!
二、循环的结束条件是否能知足要求!
三、query.setLimit(size) 每次循环查询的pageSize最好放在循环内部,尤为调用的是同一个应用的方法的时候。
下面给出通用的伪代码:code
QueryParam query = new QueryParam(); query.setOffset(0); int pageSize = 2000; //必要的时候要加上orderBy,不然会漏查数据 query.setOrderBy(new OrderBy("id",OrderBy.ASC)); while (true){ query.setLimit(size); Result<List<EntityDTO>> listResult = QueryFacade.query(query); if (!listResult.isSuccess() || listResult.getData() == null){ logger.warn(">>>>>>>>>>>>查询投失败"); } if (CollectionUtils.isEmpty(listResult.getData())){ break; } List<EntityDTO> data = listResult.getData(); for (EntityDTO feeDTO : data){ //处理一些业务逻辑 } int currentSize = data.size(); if (currentSize < size) { break; } query.setGtId(data.get(data.size()-1).getId()); }
步骤比较简单,不过用得频率高,因此尤为须要注意!开发
四、SQL中的坑 |
(1)假设有表 user,有金额相关的字段:本金 amount,收益 earning,真实本金:real_amount,真实收益:real_earning,如今须要将real_amount + real_earning - ((amount + earning)的差值加到对应的real_earning,real_amount。看似很完美,实际的结果是real_earning的值先修改了,后面参与运算的值已是修改后的值了,这个sql直接让我吐血了,也背了一个很大的锅。get
UPDATE USER SET real_earning = real_earning + ( real_amount + real_earning - ((amount + earning)), real_amount = real_amount + ( real_amount + real_earning - ((amount + earning)) WHERE end_benefit_date >= '2018-10-19' AND end_benefit_date <= '2018-10-21' AND settle_status IN (0, 1) AND ( real_amount + real_earning - ((amount + earning)) > 0