使用res:bean屬性復(fù)制避免null值覆蓋版本
res:bean屬性復(fù)制避免null值覆蓋版本
前言
- 最近在設(shè)計通用的 Service 和 Controller 層
- 設(shè)計過程中涉及到實(shí)體對象(JPA)的更新操作
- 原因1:JPA 的 saveAndFlush 方法會將把 null 也更新上去
- 原因2:spring 的 BeanUtils.copyBeanProperties 方法會把 src 所有屬性值包括 null 覆蓋到 dest,不符合要求
- 所以,利用反射,寫一個屬性復(fù)制方法代替 spring 的工具方法
- 另外,controller 層使用 @ModelAttribut 也可以解決這個問題,這就是另一個主題
代碼 copyBeanPropertiesIgoreNull
/** * 對象屬性值拷貝,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ù)變更的時候,使用BeanUtils.copyProperties(newdata,dbdata)進(jìn)行數(shù)據(jù)變更的時候,由于前臺展示的數(shù)據(jù)不完整。
導(dǎo)致前臺傳遞的數(shù)據(jù)將后臺的原始數(shù)據(jù)全部覆蓋掉。那么如何解決這種null值的覆蓋呢。BeanUtils.copyProperties()可以通過添加可變長參數(shù)忽略掉具體的某些不需要更新的字段。比如BeanUtils.copyProperties(newdata,dbdata,“id”,“password”)。
那么如果字段比較多,使用上面的方法簡直要瘋掉了。有沒有更好的方法呢?
可以自己拓展一個方法,匯總值為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); }
通過這個方法就可以將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
好了問題解決!以上為個人經(jīng)驗(yàn),希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
Netty分布式pipeline管道創(chuàng)建方法跟蹤解析
這篇文章主要為大家介紹了Netty分布式pipeline管道創(chuàng)建方法跟蹤解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-03-03Java實(shí)現(xiàn)Fast DFS、服務(wù)器、OSS上傳功能
這篇文章主要介紹了Java實(shí)現(xiàn)Fast DFS、服務(wù)器、OSS上傳功能,在實(shí)際的業(yè)務(wù)中,可以根據(jù)客戶的需求設(shè)置不同的文件上傳需求,支持普通服務(wù)器上傳+分布式上傳(Fast DFS)+云服務(wù)上傳OSS(OSS),需要的朋友可以參考下2024-04-04Spring?Boot項(xiàng)目如何使用Maven打包并帶上依賴
在這篇博客中,介紹如何使用Maven將Spring?Boot項(xiàng)目及其依賴項(xiàng)打包成一個可執(zhí)行的jar文件。我們將使用Spring?Boot的spring-boot-maven-plugin插件來完成這個任務(wù),感興趣的朋友跟隨小編一起看看吧2023-06-06Java面試題沖刺第二十八天--數(shù)據(jù)庫(5)
這篇文章主要為大家分享了最有價值的三道關(guān)于數(shù)據(jù)庫的面試題,涵蓋內(nèi)容全面,包括數(shù)據(jù)結(jié)構(gòu)和算法相關(guān)的題目、經(jīng)典面試編程題等,感興趣的小伙伴們可以參考一下2021-09-09SpringBoot基于Redis實(shí)現(xiàn)短信登錄的操作
驗(yàn)證碼登錄是非常常見的一種登錄方式,能夠簡化用戶登錄的過程,本文主要介紹了SpringBoot基于Redis實(shí)現(xiàn)短信登錄的操作,具有一定的參考價值,感興趣的可以了解一下2023-12-12