记一次lombok踩坑记
引言
今天中午正在带着耳机遨游在代码的世界里,被运营在群里@了,气冲冲的反问我最近有删生产的用户数据的吗?我肯定客气的回答道没有呀?生产的数据我怎么能随随便便可以删除,这可是公司的红线,再说了我也没有数据库的删除权限啊,不过查询权限还是有的。赶紧登上堡垒机,然后去生产数据库查一下数据,查了一下数据是还在的,吓死了,数据还在问题就不大了,无非就是应用程序出问题了,赶紧打开代码查看下,为什么会少了一条用户数据,看了下代码貌似没啥问题就是比较简单的一个逻辑,直接从DB通过分页查询数据给到前端,然后前端负责展示,没有啥复杂的逻辑。心想肯定是前端的问题,肯定是他少展示了数据,立马把问题也甩给了他,让他帮忙配合一起看看是否是前端的问题,然后自己也仔细看看代码,不到一分钟前端说他展示的数据没有问题,都是后端给到的,没有漏掉展示的。那就是后端的bug了罗。肉眼望去觉得可能出问题的就是分页导致的数据丢了。不过这个分页插件是全公司都在用,应该不至于出问题把,找不到问题只能让测试帮忙在测试环境试试,看看是否可以复现。
测试环境复现
仔细看了一眼,居然有个去重的方法,去重逻辑也比较简单就是把list通过转为set去下重,看下来应该就是这个去重方法有问题了
大致写了单元测试模仿了下生产的数据,大致逻辑如下:
public static void main(String[] args) {
Set<UserDTO> userSet = new HashSet<>();
UserDTO userDTO = new UserDTO();
userDTO.setId(1);
userDTO.setUserName("java金融");
UserDTO userDTO1 = new UserDTO();
userDTO1.setId(2);
userDTO1.setUserName("java金融");
userSet.add(userDTO);
userSet.add(userDTO1);
System.out.println(userSet.size());
System.out.println(userDTO1.equals(userDTO));
}
@Data
static class UserDTO extends BaseDTO {
private String userName;
}
@Data
static class BaseDTO {
private Integer id;
}
我们可以输出结果set集合的长度是1,user1和user2 是相等的,明明两个user的ID是不一样的,为何会相等,我们知道set可以去重
是因为Set的操作,都是通过操作map来实现的,set的add其实就是调用map的put方法,map的put方法我相信大家应该都去看过其源码,这里就不详细再说了,大概流程就是通过key通过hash算法定位到数组的下标,先判断key的hash是否相等,如果相等再去判断key的value相等,如果都相等就会覆盖原来的值。我们上面这个例子就是对象的hash和value都相等导致,但是我们的两个对象user1和user2 应该是不等的,因为ID不等,那为什么会相等列?我们仔细看下上面的代码,我们使用了lombok里面@Data注解,我们可以看看这个注解帮我们生成了哪些方法


通过上面的对比我们可以看出@Data注解帮我们生成了 注在类上,提供类的get、set、equals、hashCode、canEqual、toString方法,这个注解确实比较方便。上面那个bug 就是因为它生成的equals方法有问题,我们可以把上述代码编译下,然后把class 里面生成的equals方法拷贝出来看看

通过上述生成的代码我们可以看出equals方法只比较了userName这个字段,也就是当前类的字段,并没有去比较父类的字段,这就是导致两个对象相等的原因,我们既然找到问题了,那解决问题就比较简单。
解决问题
- 手动重写
equals和hashCode方法,这种方法肯定是不推荐的,我们既然用了lombok就是为了解放我们的双手,是代码变得更加简洁。 - 在比较的类上加上
@EqualsAndHashCode(callSuper = true) callSuper = true会包含父类的equals和hashCode方法
我们可以对比下加上@EqualsAndHashCode(callSuper = true)和没有加上这个注解生成的equals方法的代码差异。

差异点还是很明显的,加入了@EqualsAndHashCode(callSuper = true)会去调用父类的equals方法比较,所以这个注解也能够解决这个问题。 - 这样加上注解确实可以解决问题,但是每个类上面都要加上这个注解,这也是个体力活。我们可以再找找其他的方案,例如有没有比如配置文件设置下什么的,然后就能全局生效了。最终通过查询资料发现我们我们写一个
lombok.config的配置文件放在我们项目的根目录下面,内容写上lombok.equalsAndHashCode.callSuper = call效果等同于@EqualsAndHashCode(callSuper = true),这样的话我们就不需要为每个类都去加上这个注释了,相当于在这个项目下面只要用到了@Data注解的类都会为其加上@EqualsAndHashCode(callSuper = true)通过配置文件的方式就可以全局生效。
总结
我们再来回顾下上面的问题,归根结底还是由于对象的
equals方法使用不当引起的,所以我们在如果在判断自定义对象业务判断相等的时候需要去重写下hashCode和equals方法,重写的时候我们可以通过idea来生成,生成后最好还是去看一眼,看看生成的是否符合我们的业务要求。我们在工作中操作一些常见的容器类比如Set、Map等来实现一些我们自己的业务,我们还是有必要去看看它们的源码的,就比如我们通过Set来进行去重,如果我们是使用的自定义对象的话,如果没有重写
hashCode和equals方法的话,去重就会去不成功,我们只有了解了它,才能真正的去用好它。在关于hashCode和equals阿里巴巴开发手册也有明确的说到

lombok用起来还是挺爽的,但是还是有一些细节需要稍微注意下。使用前可以大概的去看看它的官网提供的内容,不然出现莫名其妙的问题你都不知道如何下手。这个就有点类似于我们使用SpringBoot一样,用起来非常爽,但是如果遇到莫名其妙的bug解决起来就比较头疼。最后我们再来回顾几道关于
hashCode和equals的比较常见的面试题?其实如果我们只要真正看过HashMap的源码的话,这下面几个面试题还是非常简单的。
什么情况下需要我们去重写 方法?
如果只重写equals方法不重写HashCode可以吗?
equals ,== 和hashcode()的区别?
。
结束
- 由于自己才疏学浅,难免会有纰漏,假如你发现了错误的地方,还望留言给我指出来,我会对其加以修正。
- 如果你觉得文章还不错,你的转发、分享、赞赏、点赞、留言就是对我最大的鼓励。
- 感谢您的阅读,十分欢迎并感谢您的关注。
记一次lombok踩坑记的更多相关文章
- Spark踩坑记——Spark Streaming+Kafka
[TOC] 前言 在WeTest舆情项目中,需要对每天千万级的游戏评论信息进行词频统计,在生产者一端,我们将数据按照每天的拉取时间存入了Kafka当中,而在消费者一端,我们利用了spark strea ...
- Spark踩坑记——数据库(Hbase+Mysql)
[TOC] 前言 在使用Spark Streaming的过程中对于计算产生结果的进行持久化时,我们往往需要操作数据库,去统计或者改变一些值.最近一个实时消费者处理任务,在使用spark streami ...
- 【踩坑记】从HybridApp到ReactNative
前言 随着移动互联网的兴起,Webapp开始大行其道.大概在15年下半年的时候我接触到了HybridApp.因为当时还没毕业嘛,所以并不清楚自己未来的方向,所以就投入了HybridApp的怀抱. Hy ...
- Spark踩坑记——共享变量
[TOC] 前言 Spark踩坑记--初试 Spark踩坑记--数据库(Hbase+Mysql) Spark踩坑记--Spark Streaming+kafka应用及调优 在前面总结的几篇spark踩 ...
- Spark踩坑记——从RDD看集群调度
[TOC] 前言 在Spark的使用中,性能的调优配置过程中,查阅了很多资料,之前自己总结过两篇小博文Spark踩坑记--初试和Spark踩坑记--数据库(Hbase+Mysql),第一篇概况的归纳了 ...
- djangorestframework+vue-cli+axios,为axios添加token作为headers踩坑记
情况是这样的,项目用的restful规范,后端用的django+djangorestframework,前端用的vue-cli框架+webpack,前端与后端交互用的axios,然后再用户登录之后,a ...
- HttpWebRequest 改为 HttpClient 踩坑记-请求头设置
HttpWebRequest 改为 HttpClient 踩坑记-请求头设置 Intro 这两天改了一个项目,原来的项目是.net framework 项目,里面处理 HTTP 请求使用的是 WebR ...
- vue踩坑记
vue踩坑记 易错点 语法好难啊qwq 不要把'data'写成'date' 在v-html/v-bind中使用vue变量时不需要加变量名 在非vue事件中使用vue中变量时需要加变量名 正确 < ...
- 【bug记录】OS Lab4 踩坑记
OS Lab4 踩坑记 Lab4在之前Lab3的基础上,增加了系统调用,难度增加了很多.而且加上注释不详细,开玩笑的指导书,自己做起来困难较大.也遇到了大大小小的bug,调试了一整天. 本文记录笔者在 ...
随机推荐
- Javaweb:Servlet
servlet简介 Servlet(Server Applet)是 Java Servlet 的简称,是使用 Java 语言编写的运行在服务器端的程序.具有独立于平台和协议的特性,主要功能在于交互式地 ...
- 使用有道云笔记还是github写笔记的优缺点对比
有道云笔记的优点 在手机上编辑笔记,有道云的体验更好,建议:新建普通类型笔记而不是markdown笔记,因为md在手机上的编辑体验并不好 插入图片方便无需考虑图床 可以把笔记分享到社交平台(QQ,微信 ...
- 查找文件与cron计划任务
查找文件 • 根据预设的条件递归查找对应的文件 find [目录] [条件1] [-a|-o] [条件2] ... -type 类型(f文件.d目录.l快捷方式) -name "文档名称 ...
- 【曹工杂谈】Mysql客户端上,时间为啥和本地差了整整13个小时,就离谱
瞎扯一点非技术 本来今天上午就打算写的,结果中途被别的事吸引了注意力,公司和某保险公司合作推了一个医疗保险,让我们给父母买,然后我研究了半天条款:又想起来之前买的支付宝那个好医保,也买了两年多了,但是 ...
- AVAssetWriter视频数据编码
AVAssetWriter介绍 可以通过AVAssetWriter来对媒体样本重新做编码. 针对一个视频文件,只可以使用一个AVAssetWriter来写入,所以每一个文件都需要对应一个新的AVAss ...
- 【linux】驱动-14-异步通知
目录 前言 14. 异步通知 14.1 异步通知的一些概念 14.2 Linux 信号 14.3 信号接收 14.4 使用流程 14.4.1 参考流程图 14.4.2 分析&编程步骤 14.4 ...
- Binding(二):控件关联和代码提升
上节我们讲到,使用Binding,我们可以关联后台代码中的属性,在某些情况下,我们可能需要将两个控件关联起来,借助Binding,我们也可以轻松的实现. 关联控件 设想这样一个场景,界面中有个Chec ...
- 一个例子让你秒懂 Qt Creator 编译原理
小北师兄作品 首发于微信公众号 小北师兄 微信 ID: ncuneupa 由于排版原因,文章可能读起来不太清晰,如果想看更好的排版,可以来我的公众号:小北师兄 大家好,我是你们的小北师兄,由于工作原因 ...
- Vue前端访问控制方案
1.前端访问控制的常规处理方法 前端访问控制,一般针对界面元素dom element进行可见属性或enable属性进行控制,有权限的,相关元素可见或使能:没权限的,相关元素不可见或失能.这样用户可 ...
- ES6 学习笔记之对象的新增方法
1. Object.is() ES5 比较两个值是否相等,只有两个运算符:相等运算符(==)和严格相等运算符(===).它们都有缺点,前者会自动转换数据类型,后者的 NaN 不等于自身,以及 +0 等 ...