背景
在进行关系型数据库保存操作时,如果要修改的数据存在,则进行修改;否则,进行保存。
但如果数据修改时,仅修改我们想要修改的字段时,需要做一层拦截处理
业务
在业务中有一张CustomerCompare表
该表是用于保存多数据源对应的各自字段值信息
当前对数表字段有:
1、数据源1的status1,tierName1,points1
2、数据源2的status2,tierName2,points2
3、数据源3的status3,tierName3,points3
如果数据源1的数据变更时,在入库保存时仅修改第1点的相关字段
数据源2同理如上
数据源3同理如上
具体操作
1、先查询库中数据是否存在
2、如果数据存在,仅修改当前需要修改的字段,忽略不需要修改的字段
3、进行保存或更新操作
基于第2点,需要写一个过滤的工具类,工具类代码如下
/**
* @创建人 Eric.Lu
* @创建时间 2023/9/5
* @描述
*/
public class UpdateUtil {
/**
* 所有为空值的属性都不copy
*
* @param source
* @param target
*/
public static void copyNullProperties(Object source, Object target) {
if (Objects.isNull(target)) {
return;
}
BeanUtils.copyProperties(source, target, getNullField(source));
}
public static void copyPropertiesIgnoreFields(Object source, Object target, String... ignoreProperties) {
if (Objects.isNull(source)) {
return;
}
BeanUtils.copyProperties(source, target, ignoreProperties);
}
/**
* 获取属性中为空的字段
*
* @param target
* @return
*/
private static String[] getNullField(Object target) {
BeanWrapper beanWrapper = new BeanWrapperImpl(target);
PropertyDescriptor[] propertyDescriptors = beanWrapper.getPropertyDescriptors();
Set<String> notNullFieldSet = new HashSet<>();
for (PropertyDescriptor p : propertyDescriptors) {
String name = p.getName();
Object value = beanWrapper.getPropertyValue(name);
if (Objects.isNull(value)) {
notNullFieldSet.add(name);
}
}
String[] notNullField = new String[notNullFieldSet.size()];
return notNullFieldSet.toArray(notNullField);
}
}
保存入库部分代码如下
public void save(AllCustomerCompareMssql allCustomerCompareMssql) {
AllCustomerCompareMssql old = allCustomerCompareMssqlRepository.findOne(allCustomerCompareMssql.getCustomerSID());
//将上游传来的不为空参数(也即是要修改值)copy覆盖原始对象属性值
//并且指定一些字段不做修改,禁止修改其它数据源的字段
String[] ignoreProperties = {
"esCustomerType", "esStatus", "esAvailablePoints", "esBirthday",
"mssqlCustomerType", "mssqlStatus", "mssqlAvailablePoints", "mssqlBirthday"
};
UpdateUtil.copyPropertiesIgnoreFields(allCustomerCompareMssql, old, ignoreProperties);
allCustomerCompareMssqlRepository.save(old);
}
UpdateUtil 工具类解析
底层使用的事Spring–BeanUtils工具类copyProperties方法
Spring的BeanUtils工具类的用法简介
我们经常需要将不同的两个对象实例进行属性复制,比如将DO对象进行属性复制到DTO,这种转换最原始的方式就是手动编写大量的 get/set代码,很繁琐。为了解决这一痛点,就诞生了一些方便的类库,常用的有 Apache的 BeanUtils,Spring的 BeanUtils, Dozer,Orika等拷贝工具。
由于Apache的BeanUtils的性能很差,强烈不建议使用。阿里巴巴Java开发规约插件上也明确指出:“Ali-Check | 避免用Apache Beanutils进行属性的copy。”
Spring的BeanUtils方法
方法 | 说明 |
---|---|
BeanUtils.copyProperties(source, target); | source对应的对象成员赋值给target对应的对象成员 |
BeanUtils.copyProperties(source, target, “id”, “time”); | 忽略拷贝某些字段。本处是忽略:id与time |
Spring的BeanUtils方法注意事项
- 浅拷贝: 对基本数据类型进行值传递,对引用数据类型,使用其引用地址,不拷贝其内容,此为浅拷贝
- 深拷贝: 对基本数据类型进行值传递,对引用数据类型,创建一个新的对象,并复制其内容,此为深拷贝。
浅拷贝与深拷贝
浅拷贝只复制指向某个对象的指针,而不复制对象本身,新旧对象还是共享同一块内存。但深拷贝会另外创造一个一模一样的对象,新对象跟原对象不共享内存,修改新对象不会改到原对象。