共计 2917 个字符,预计需要花费 8 分钟才能阅读完成。
引言
明天中午正在带着耳机漫游在代码的世界里,被经营在群里 @
了,气冲冲的反诘我最近有删生产的用户数据的吗?我必定客气的答复道没有呀?生产的数据我怎么能随随便便能够删除,这可是公司的红线,再说了我也没有数据库的删除权限啊,不过查问权限还是有的。连忙登上堡垒机,而后去生产数据库查一下数据,查了一下数据是还在的,吓死了,数据还在问题就不大了,无非就是应用程序出问题了,连忙关上代码查看下,为什么会少了一条用户数据,看了下代码貌似没啥问题就是比较简单的一个逻辑,间接从 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()
的区别?
完结
- 因为本人满腹经纶,难免会有纰漏,如果你发现了谬误的中央,还望留言给我指出来, 我会对其加以修改。
- 如果你感觉文章还不错,你的转发、分享、赞叹、点赞、留言就是对我最大的激励。
- 感谢您的浏览, 非常欢送并感谢您的关注。