使用res:bean屬性復(fù)制避免null值覆蓋版本
res:bean屬性復(fù)制避免null值覆蓋版本
前言
- 最近在設(shè)計(jì)通用的 Service 和 Controller 層
- 設(shè)計(jì)過程中涉及到實(shí)體對(duì)象(JPA)的更新操作
- 原因1:JPA 的 saveAndFlush 方法會(huì)將把 null 也更新上去
- 原因2:spring 的 BeanUtils.copyBeanProperties 方法會(huì)把 src 所有屬性值包括 null 覆蓋到 dest,不符合要求
- 所以,利用反射,寫一個(gè)屬性復(fù)制方法代替 spring 的工具方法
- 另外,controller 層使用 @ModelAttribut 也可以解決這個(gè)問題,這就是另一個(gè)主題
代碼 copyBeanPropertiesIgoreNull
/**
* 對(duì)象屬性值拷貝,null 值覆蓋修復(fù)版
* @param beanSrc
* @param beanDest
*/
public static void copyBeanPropertiesIgoreNull(Object beanSrc, Object beanDest){
Class<?> clazzSrc = beanSrc.getClass();
Class<?> clazzDest = beanDest.getClass();
//獲取所有屬性,包括私有的和繼承的
List<Field> allFields = getAllFields(beanSrc);
try {
for(Field field:allFields) {
String fieldName = field.getName(); //屬性名
if("serialVersionUID".equals(fieldName)) {
continue;
}
PropertyDescriptor pd1 = getPropertyDescriptor(fieldName, clazzSrc);
if(pd1!=null) {
Method rMethod = pd1.getReadMethod();
if(rMethod!=null) {
Object fieldValue = rMethod.invoke(beanSrc); //屬性值,引用類型,所以一般實(shí)體的屬性用 Integer instead of int
if(fieldValue!=null) { //關(guān)鍵:屬性值為 null 就不要覆蓋了
PropertyDescriptor pd2 = getPropertyDescriptor(fieldName, clazzDest);
if(pd2!=null) {
Method wMethod = pd2.getWriteMethod();
if(wMethod!=null) {
wMethod.invoke(beanDest, fieldValue);
}
}
}
}
}
}
} catch (IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
throw new RuntimeException(">> copyPropertiesIgoreNull exception", e);
}
}
BeanUtils.copyProperties解決null值覆蓋
這里使用的是Spring提供的BeanUtils的工具類(commons-lang3可參考)。在做數(shù)據(jù)變更的時(shí)候,使用BeanUtils.copyProperties(newdata,dbdata)進(jìn)行數(shù)據(jù)變更的時(shí)候,由于前臺(tái)展示的數(shù)據(jù)不完整。
導(dǎo)致前臺(tái)傳遞的數(shù)據(jù)將后臺(tái)的原始數(shù)據(jù)全部覆蓋掉。那么如何解決這種null值的覆蓋呢。BeanUtils.copyProperties()可以通過添加可變長參數(shù)忽略掉具體的某些不需要更新的字段。比如BeanUtils.copyProperties(newdata,dbdata,“id”,“password”)。
那么如果字段比較多,使用上面的方法簡直要瘋掉了。有沒有更好的方法呢?
可以自己拓展一個(gè)方法,匯總值為null的數(shù)據(jù)
public static String[] getNullPropertyNames (Object source) {
final BeanWrapper src = new BeanWrapperImpl(source);
PropertyDescriptor[] pds = src.getPropertyDescriptors();
Set<String> emptyNames = new HashSet<String>();
for(PropertyDescriptor pd : pds) {
Object srcValue = src.getPropertyValue(pd.getName());
if (srcValue == null) emptyNames.add(pd.getName());
}
String[] result = new String[emptyNames.size()];
return emptyNames.toArray(result);
}
通過這個(gè)方法就可以將null數(shù)據(jù)找到,然后在可變長參數(shù)中使用方法即可。
BeanUtils.copyProperties(newdata,dbdata,getNullPropertyNames(newdata));
附demo:
public static void main(String[] args) {
U u = new U("1","zhangsan",null,null,null,"11");
String[] nullPropertyNames = getNullPropertyNames(u);
for (String nullPropertyName : nullPropertyNames) {
System.out.println(nullPropertyName);
}
}
public static String[] getNullPropertyNames (Object source) {
final BeanWrapper src = new BeanWrapperImpl(source);
PropertyDescriptor[] pds = src.getPropertyDescriptors();
Set<String> emptyNames = new HashSet<String>();
for(PropertyDescriptor pd : pds) {
Object srcValue = src.getPropertyValue(pd.getName());
if (srcValue == null) emptyNames.add(pd.getName());
}
String[] result = new String[emptyNames.size()];
return emptyNames.toArray(result);
}
class U {
private String id;
private String name;
private String field1;
private String field2;
private String field3;
private String field4;
public U(String id, String name) {
this.id = id;
this.name = name;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getField1() {
return field1;
}
public void setField1(String field1) {
this.field1 = field1;
}
public String getField2() {
return field2;
}
public void setField2(String field2) {
this.field2 = field2;
}
public String getField3() {
return field3;
}
public void setField3(String field3) {
this.field3 = field3;
}
public String getField4() {
return field4;
}
public void setField4(String field4) {
this.field4 = field4;
}
public U(String id, String name, String field1, String field2, String field3, String field4) {
this.id = id;
this.name = name;
this.field1 = field1;
this.field2 = field2;
this.field3 = field3;
this.field4 = field4;
}
}
打印的結(jié)果:
field1
field2
field3
好了問題解決!以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
Java并發(fā)工具類之CountDownLatch詳解
這篇文章主要介紹了Java并發(fā)工具類之CountDownLatch詳解,CountDownLatch可以使一個(gè)獲多個(gè)線程等待其他線程各自執(zhí)行完畢后再執(zhí)行,CountDownLatch可以解決那些一個(gè)或者多個(gè)線程在執(zhí)行之前必須依賴于某些必要的前提業(yè)務(wù)先執(zhí)行的場景,需要的朋友可以參考下2023-12-12
Java從源碼看異步任務(wù)計(jì)算FutureTask
這篇文章主要介紹了Java從源碼看異步任務(wù)計(jì)算FutureTask,F(xiàn)utureTask就能夠很好的幫助我們實(shí)現(xiàn)異步計(jì)算,并且可以實(shí)現(xiàn)同步獲取異步任務(wù)的計(jì)算結(jié)果,具體是怎樣實(shí)現(xiàn)的,下面我們就一起來學(xué)習(xí)下面文章的具體內(nèi)容吧2022-04-04
SpringBoot實(shí)戰(zhàn)之高效使用枚舉參數(shù)(原理篇)案例詳解
這篇文章主要介紹了SpringBoot實(shí)戰(zhàn)之高效使用枚舉參數(shù)(原理篇)案例詳解,本篇文章通過簡要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下2021-09-09
在idea2023中使用SpringBoot整合Lombok全過程及詳細(xì)用法
Lombok項(xiàng)目是一個(gè)java庫,它可以自動(dòng)插入到編輯器和構(gòu)建工具中,增強(qiáng)java的性能,本文詳細(xì)給大家介紹了在idea2023中使用SpringBoot整合Lombok全過程及詳細(xì)用法,需要的朋友可以參考下2023-09-09
Java中JSON字符串反序列化(動(dòng)態(tài)泛型)
文章討論了在定時(shí)任務(wù)中使用反射調(diào)用目標(biāo)對(duì)象時(shí)處理動(dòng)態(tài)參數(shù)的問題,通過將方法參數(shù)存儲(chǔ)為JSON字符串并進(jìn)行反序列化,可以實(shí)現(xiàn)動(dòng)態(tài)調(diào)用,然而,這種方式容易導(dǎo)致內(nèi)存溢出(OOM),這篇文章主要介紹了JSON字符串反序列化?動(dòng)態(tài)泛型,需要的朋友可以參考下2024-12-12
Java程序初始化啟動(dòng)自動(dòng)執(zhí)行的三種方式
這篇文章主要介紹了Java程序初始化啟動(dòng)自動(dòng)執(zhí)行的三種方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-01-01

