前言
在咱们日常coding中,很多时候咱们都是把接口代码全副写完再进行测试,在咱们查看代码的时候,经常很有信念查看了多遍当前,感觉本人的代码必定没啥问题,逻辑很清晰、排版很参差、公共办法抽的很好,很有信念的去调本人的接口,不料动不动一会500、一会400,就差报250~,其实咱们经常的我认为,只是我认为,执行后果会教你做人,事实证明代码不须要只是看着难看的,还是须要看执行后果的,上面我分享一些日常会踩到坑的中央
八大常见bug
Top1 申请参数为空
经常听到,数据库明明有数据呀,我把sql粘进去再mysql执行也有数据呀,为什么我用mybatis执行sql就没数据呢。
POST申请,如参数为字符串,申请到后端接口只能有一个后果,那就是 parameter参数为NULL,影响行数也0,POST申请须要联合 RequestBody应用,所以POST申请的入参是放到body外面的。
Top2 StringUtils.isEmpty 判断对象为空
User user = User(new LambdaQueryWrapper<User>()
.eq(User::getUserId, userId)
if (!StringUtils.isEmpty(user))
复制代码
乍一看没啥问题,代码也没报错,判断user不为空,然而执行的时候往往就会有问题。
对象签名对于个别的属性解决代码很有用,这些代码通常解决字符串,但通常必须遍历对象,因为属性也可能是根本值对象。
Note: If the object is typed to String upfront, prefer hasLength(String) or hasText(String) instead.
Params:
str – the candidate object (possibly a String)
Since:
3.2.1
See Also:
hasLength(String), hasText(String)
public static boolean isEmpty(@Nullable Object str) {
return (str == null || "".equals(str));
}
复制代码
看源码的解释,通常是解决字符串,判断对象须要重写isEmpty办法。
Top3 sql if 函数判断
if(p.transport_fee =1,p.payable_amount,s.actual_goods_number * p.goods_price ) as payableAmount,
复制代码
失常状况下如果transport_fee =1 则会取p.payable_amount字段,反之则会取前面的运算后果,乍一看又没啥故障。
然而 如果这个字段是NULL,那么就BBQ了,mysql if函数判断就不会判断到字段为NULL的数据。所以最好在创立这种须要逻辑判断的字段,设置默认值。
Top4 Mybatis plus lambda 表达式AND 和OR连用
其实咱们sql想要的成果是这样
select
<include refid="baseColumnList"/>
from slaughter_batch_statement
where
enable = 1
and (slaughter_statement_no
=#{dto.slaughterStatementNo
} or slaughter_statement_no is null)
复制代码
LambdaQueryWrapper 查问谬误用法:
SlaughterBatchStatement slaughterBatchStatement = slaughterBatchStatementMapper.selectOne(new LambdaQueryWrapper<SlaughterBatchStatement>().
eq(SlaughterBatchStatement::getSlaughterStatementNo, slaughterStatementNo).or().eq(SlaughterBatchStatement::getBatchNo,slaughterStatementNo));
复制代码
or().eq()的用法会在sql,and 条件后追加or,而不能
(supplier_bill_statement_no =‘xxxxxxx’ or supplier_bill_statement_no is null) 像这样用括号括起来,括号括起来和不括起来的条件判断齐全不一样。
LambdaQueryWrapper or正确用法:
SlaughterBatchStatement slaughterBatchStatement = slaughterBatchStatementMapper.selectOne(new LambdaQueryWrapper<SlaughterBatchStatement>().
eq(SlaughterBatchStatement::getEnable, 1).and (item -> item.eq(SlaughterBatchStatement::getSlaughterStatementNo, "xxxxx") .or().eq(SlaughterBatchStatement::getSlaughterStatementNo, null)));
复制代码
Top5 BigDecimal 保留两位小数
有时候做金额计算的时候,应用BigDecimal常常会在计算后保留两位小数。
BigDecimal receivableAmountNum = receivableFeeAmountNum.add(slaughterReceivableAmountNum).setScale(2, RoundingMode./HALF_UP/);
复制代码
你认为光代码设置保留两位小数就行了么,数据库对字段的设置也是须要加两位小数限度的。
如果你不设置,那么保留到库外面的数据仍旧是四舍五入后的整数。
Top6 Spring中一个类调用另一个类Null指针
新建一个类,而后在另一个类去调用,创立了一个类,定义了办法。
谬误调用形式:这是空指针的罪魁祸首
正确的调用形式:
Spring中间接new 进去的对象无奈通过@Autowired 的形式注入到spring的Bean容器外面。
Top7 Bean. copyProperties 引发的问题
在开发的过程中,遇到DTO和VO之间的转换,为了防止写很多的get、set,通常会应用Bean. copyProperties对对象进行赋值转换。
SlaughterBatchStatementResVO slaughterBatchStatementResVO = new SlaughterBatchStatementResVO();
SlaughterBatchStatement slaughterBatchStatement = slaughterBatchStatementMapper.selectOne(new LambdaQueryWrapper<SlaughterBatchStatement>().
eq(SlaughterBatchStatement::getSlaughterStatementNo, slaughterUpdateReqDTO.getSlaughterStatementNo()));
//对象进行copy
BeanCopyUtil.copyProperties(slaughterBatchStatement, slaughterBatchStatementResVO);
复制代码
BeanCopyUtil.copyProperties 这里的copy属于浅拷贝,两个对象都指向同一个指针,所以在对其中一个对象属性进行批改时,因为指针都是一样,那么另外一个对象的属性也会更改,就会呈现一些问题。
当然Bean的copy还有另外一种形式就是深拷贝,通过new新的对象进行copy
List<ReceivableFeeVO> receivableFeeResList = BeanCopyUtil.copyListProperties(slaughterUpdateReqDTO.getReceivableFees(), ReceivableFeeVO::new);
复制代码
能够通过继承BeanUtils重写Bean拷贝的工具类实现
Top8 List.contains Interger 无奈匹配
@Data
public class SlaughterBatchExportRequestDTO {
private List<String> slaughterBatchExportType;
}
if (slaughterBatchExportRequestDTO.getSlaughterBatchExportType().contains(2)) {
// ...
}
复制代码
有时候一不留神就手抖写错了,contains的入参是一个Object类型,所以入参数是任何类型都不报错,然而能不能匹配到又是另外一回事了。
List汇合去匹配int类型是否存在,那后果可想而已永远不会进if判断。
总结
刚工作的时候常常会因为本人的大意,呈现一些比拟低级的Bug,现在曾经多少混成老油条了,所以去写更加高级的Bug了,总而言之写代码不光要逻辑思路正确,最要害的我认为还是仔细,重复的review本人的代码,并且进行自测,如果还有更加常见的bug的欢送评论区留言,踩坑踩的多了,填坑填的多了,缓缓就变成了路~