一、需求
往ES中存储通讯录信息,增量操作。
思路:每一次插入前,先查询ES中的数据,看看是否有记录:没有记录则直接插入;有记录则遍历记录,看看每一个record是否包含在要新增的List里面,不在则add进去。
二、实现
1、最初方式及问题
1)bean类
package com.xxx.cash.entity.deviceinfo;
import com.fasterxml.jackson.annotation.JsonProperty;
import org.apache.commons.lang3.StringUtils;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* 设备通讯录信息
*/
public class ContactsInfo extends DeviceInfoBase {
private List records = new ArrayList<>();
public List getRecords() {
return records;
}
public void setRecords(List records) {
if (records == null)
this.records = Collections.emptyList();
else
this.records = records;
}
public static class AddressBook {
private String name; // 姓名
private String phone;
@JsonProperty(value = "home_phone")
private String homePhone; // 住宅电话
@JsonProperty(value = "work_phone")
private String workPhone; // 工作电话
@JsonProperty(value = "last_updated_time")
private String lastUpdatedTime;
@JsonProperty(value = "last_time_contacted")
private String lastTimeContacted;
public String getLastTimeContacted() {
return lastTimeContacted;
}
public void setLastTimeContacted(String lastTimeContacted) {
this.lastTimeContacted = lastTimeContacted;
}
public String getLastUpdatedTime() {
return lastUpdatedTime;
}
public void setLastUpdatedTime(String lastUpdatedTime) {
this.lastUpdatedTime = lastUpdatedTime;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
public String getHomePhone() {
return homePhone;
}
public void setHomePhone(String homePhone) {
this.homePhone = homePhone;
}
public String getWorkPhone() {
return workPhone;
}
public void setWorkPhone(String workPhone) {
this.workPhone = workPhone;
}
}
}
2)实现类
package com.xxx.cash.service.impl.deviceinfo;
import com.xxx.cash.commons.EsEnum;
import com.xxx.cash.entity.deviceinfo.ContactsInfo;
import com.fasterxml.jackson.core.type.TypeReference;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@Service("ContactsSaveServiceImpl")
public class ContactsSaveServiceImpl extends AbstractDeviceDataSaveService {
@Override
Class getEntityClass() {
return ContactsInfo.class;
}
@Override
String getESType() {
return EsEnum.CONTACT.getType();
}
@Override
void processEntity(ContactsInfo contactsInfo) {
String key = contactsInfo.getUserID() + "-" + String.valueOf(contactsInfo.getType()) + "-" + contactsInfo.getImei();
//查询ES中记录
ContactsInfo oldContacts = esdao.getMapByKey(getIndex(), getESType(), key, new TypeReference() {
});
if (oldContacts != null && oldContacts.getRecords() != null) {
List newRecords = contactsInfo.getRecords();
List oldRecords = oldContacts.getRecords();
for (ContactsInfo.AddressBook record : oldRecords) {
//新纪录不包含有老数据,则add
if (!newRecords.contains(record)) {
newRecords.add(record);
}
}
}
}
}
3)问题
ES中的同一个人的通讯录数据一直在增大,查询发现里面很多重复数据。经过校验发现,每一次新存储请求都全量存储,所以数据随着请求次数的增加而增大。
4)排查
通过debug发现,这个newRecords.contains(record)一直为false,newRecords.add(record)操作一直在做。
①查看源码
java.util.ArrayList的contains()方法
public boolean contains(Object o) {
return indexOf(o) >= 0;
}
public int indexOf(Object o) {
if (o == null) {
for (int i = 0; i < size; i++)
if (elementData[i]==null)
return i;
} else {
for (int i = 0; i < size; i++)
if (o.equals(elementData[i]))
return i;
}
return -1;
}
indexOf()方法中用到equals()方法,在看看equals()怎么实现的:
public boolean equals(Object obj) {
return (this == obj);
}
从源码能看到,equals方法用的是Object类的,==双等号比较的是对象的地址值。
所以,这下子知道为什么newRecords.contains(record)一直为false了,对象的地址值不一样。
2、修复
刚开始的思路有几个:
1)重写equals方法,然后想着什么去调用这个indexOf怎么调用equals,contains怎么调用indexOf
2)把新老数据先弄到一个list里面,然后再去遍历,存到set里面去重,然后在存到newRecords里面。
实践:
1)越想越复杂,就没实现了,但是在bean类重写了equals方法
2)在处理中,发现set进去后,对象不会去重,原因还没排查。但是在调试中,有了意外收获。
最终:
重写equals方法:
package com.xxx.cash.entity.deviceinfo;
import com.fasterxml.jackson.annotation.JsonProperty;
import org.apache.commons.lang3.StringUtils;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* 设备通讯录信息
*/
public class ContactsInfo extends DeviceInfoBase {
private List records = new ArrayList<>();
public List getRecords() {
return records;
}
public void setRecords(List records) {
if (records == null)
this.records = Collections.emptyList();
else
this.records = records;
}
public static class AddressBook {
private String name; // 姓名
private String phone;
@JsonProperty(value = "home_phone")
private String homePhone; // 住宅电话
@JsonProperty(value = "work_phone")
private String workPhone; // 工作电话
@JsonProperty(value = "last_updated_time")
private String lastUpdatedTime;
@JsonProperty(value = "last_time_contacted")
private String lastTimeContacted;
public String getLastTimeContacted() {
return lastTimeContacted;
}
public void setLastTimeContacted(String lastTimeContacted) {
this.lastTimeContacted = lastTimeContacted;
}
public String getLastUpdatedTime() {
return lastUpdatedTime;
}
public void setLastUpdatedTime(String lastUpdatedTime) {
this.lastUpdatedTime = lastUpdatedTime;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
public String getHomePhone() {
return homePhone;
}
public void setHomePhone(String homePhone) {
this.homePhone = homePhone;
}
public String getWorkPhone() {
return workPhone;
}
public void setWorkPhone(String workPhone) {
this.workPhone = workPhone;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null || getClass() != obj.getClass()) {
return false;
}
AddressBook add = (AddressBook) obj;
return
StringUtils.equals(homePhone, add.homePhone) &&
StringUtils.equals(name, add.name) &&
StringUtils.equals(phone, add.phone) &&
StringUtils.equals(workPhone, add.workPhone) &&
StringUtils.equals(lastUpdatedTime, add.lastUpdatedTime) &&
StringUtils.equals(lastTimeContacted, add.lastTimeContacted);
}
}
}
在调试中发现,contains方法下的indexOf方法会自动去调用重写的equals方法,真是爽,之前的纠结都解决了。
再次运行代码,同一个请求,发现es的存储结果不会增加了,达到了去重的效果了。
注意:
在此处,判断两个属性是否相等时,不要用==(==比较的是对象的地址值),也不要用homePhone.equals(add.homePhone)(容易报空指针异常)
-完-