BeanUtils.copyProperties()所有的空值不復(fù)制問題
BeanUtils.copyProperties()所有的空值不復(fù)制
第一種情況
所有為空值的屬性都不copy
直接上代碼吧~
public class UpdateUtil {
/**
* 所有為空值的屬性都不copy
*
* @param source
* @param target
*/
public static void copyNullProperties(Object source, Object target) {
BeanUtils.copyProperties(source, target, getNullField(source));
}
/**
* 獲取屬性中為空的字段
*
* @param target
* @return
*/
private static String[] getNullField(Object target) {
BeanWrapper beanWrapper = new BeanWrapperImpl(target);
PropertyDescriptor[] propertyDescriptors = beanWrapper.getPropertyDescriptors();
Set<String> notNullFieldSet = new HashSet<>();
if (propertyDescriptors.length > 0) {
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 static void main(String[] args) {
TopMenuConfigEntity topMenuConfigEntity1 = new TopMenuConfigEntity();
topMenuConfigEntity1.setWardCode("cat");
topMenuConfigEntity1.setTitle("animal");
TopMenuConfigEntity topMenuConfigEntity2 = new TopMenuConfigEntity();
topMenuConfigEntity2.setWardCode("dog");
UpdateUtil.copyNullProperties(topMenuConfigEntity2,topMenuConfigEntity1);
System.out.println(topMenuConfigEntity1.getTitle());
}
}
執(zhí)行main 方法后,topMenuConfigEntity1的title還是為原來的“animal”值,沒有被topMenuConfigEntity2 的空值覆蓋。
第二種情況
原對象的屬性有值,復(fù)制時指定某些字段不復(fù)制
調(diào)BeanUtils的這個方法
public static void copyProperties(Object source, Object target, String... ignoreProperties) throws BeansException {
?? ??? ?copyProperties(source, target, null, ignoreProperties);
?? ?} public static void main(String[] args) {
TopMenuConfigEntity topMenuConfigEntity1 = new TopMenuConfigEntity();
topMenuConfigEntity1.setWardCode("cat");
topMenuConfigEntity1.setTitle("animal");
topMenuConfigEntity1.setCreateTime(new Date());
TopMenuConfigEntity topMenuConfigEntity2 = new TopMenuConfigEntity();
String[] ignoreArray = new String[]{"title","createTime"};
BeanUtils.copyProperties(topMenuConfigEntity2,topMenuConfigEntity1,ignoreArray);
System.out.println("title : "+topMenuConfigEntity2.getTitle() +";createTime :" + topMenuConfigEntity2.getCreateTime());
}topMenuConfigEntity2的title 和createTime為null,沒有復(fù)制
BeanUtils.copyProperties()的用法和注意點
屬性為null也會被復(fù)制,內(nèi)部類不會復(fù)制過去
BeanUtils提供對Java反射和自省API的包裝。其主要目的是利用反射機制對JavaBean的屬性進行處理。我們知道,一個JavaBean通常包含了大量的屬性,很多情況下,對JavaBean的處理導(dǎo)致大量get/set代碼堆積,增加了代碼長度和閱讀代碼的難度。
BeanUtils是這個包里比較常用的一個工具類,這里只介紹它的copyProperties()方法。
該方法源碼如下:
public static void copyProperties(Object source, Object target) throws BeansException {
copyProperties(source, target, (Class)null, (String[])null);
}
public static void copyProperties(Object source, Object target, Class<?> editable) throws BeansException {
copyProperties(source, target, editable, (String[])null);
}
public static void copyProperties(Object source, Object target, String... ignoreProperties) throws BeansException {
copyProperties(source, target, (Class)null, ignoreProperties);
}
private static void copyProperties(Object source, Object target, Class<?> editable, String... ignoreProperties) throws BeansException {
Assert.notNull(source, "Source must not be null");
Assert.notNull(target, "Target must not be null");
Class<?> actualEditable = target.getClass();
if(editable != null) {
if(!editable.isInstance(target)) {
throw new IllegalArgumentException("Target class [" + target.getClass().getName() + "] not assignable to Editable class [" + editable.getName() + "]");
}
actualEditable = editable;
}
PropertyDescriptor[] targetPds = getPropertyDescriptors(actualEditable);
List<String> ignoreList = ignoreProperties != null?Arrays.asList(ignoreProperties):null;
PropertyDescriptor[] var7 = targetPds;
int var8 = targetPds.length;
for(int var9 = 0; var9 < var8; ++var9) {
PropertyDescriptor targetPd = var7[var9];
Method writeMethod = targetPd.getWriteMethod();
if(writeMethod != null && (ignoreList == null || !ignoreList.contains(targetPd.getName()))) {
PropertyDescriptor sourcePd = getPropertyDescriptor(source.getClass(), targetPd.getName());
if(sourcePd != null) {
Method readMethod = sourcePd.getReadMethod();
if(readMethod != null && ClassUtils.isAssignable(writeMethod.getParameterTypes()[0], readMethod.getReturnType())) {
try {
if(!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers())) {
readMethod.setAccessible(true);
}
Object value = readMethod.invoke(source, new Object[0]);
if(!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers())) {
writeMethod.setAccessible(true);
}
writeMethod.invoke(target, new Object[]{value});
} catch (Throwable var15) {
throw new FatalBeanException("Could not copy property '" + targetPd.getName() + "' from source to target", var15);
}
}
}
}
}
}如果你有兩個具有很多相同屬性的JavaBean,就可以試用該方法將sourse中的屬性copy到target中,如果sourse和target間存在名稱不相同的屬性,則BeanUtils不對這些屬性進行處理,需要程序員手動處理。
怎么樣,很方便吧!除BeanUtils外還有一個名為PropertyUtils的工具類,它也提供copyProperties()方法,作用與 BeanUtils的同名方法十分相似,主要的區(qū)別在于后者提供類型轉(zhuǎn)換功能,即發(fā)現(xiàn)兩個JavaBean的同名屬性為不同類型時,在支持的數(shù)據(jù)類型范圍內(nèi)進行轉(zhuǎn)換,而前者不支持這個功能,但是速度會更快一些。
BeanUtils支持的轉(zhuǎn)換類型如下:
* java.lang.BigDecimal * java.lang.BigInteger * boolean and java.lang.Boolean * byte and java.lang.Byte * char and java.lang.Character * java.lang.Class * double and java.lang.Double * float and java.lang.Float * int and java.lang.Integer * long and java.lang.Long * short and java.lang.Short * java.lang.String * java.sql.Date * java.sql.Time * java.sql.Timestamp
這里要注意一點,java.util.Date是不被支持的,而它的子類java.sql.Date是被支持的。因此如果對象包含時間類型的屬性,且希望被轉(zhuǎn)換的時候,一定要使用java.sql.Date類型。否則在轉(zhuǎn)換時會提示argument mistype異常。
現(xiàn)在,還有一個壞消息:使用BeanUtils的成本驚人地昂貴!我做了一個簡單的測試,BeanUtils所花費的時間要超過取數(shù) 據(jù)、將其復(fù)制到對應(yīng)的 value對象(通過手動調(diào)用get和set方法),以及通過串行化將其返回到遠程的客戶機的時間總和。所以要小心使用。
注意點一
apache和spring的工具包中都有BeanUtils,使用其中的copyProperties方法可以非常方便的進行這些工作,但在實際應(yīng)用中發(fā)現(xiàn),對于null的處理不太符合個人的需要,例如在進行修改操作中只需要對model中某一項進行修改,那么一般我們在頁面上只提交model的ID及需要修改項的值,這個時候使用BeanUtils.copyProperties會將其他的null綁定到pojo中去。
大家可以直接調(diào)用我們加工類的copyPropertiesIgnoreNull()方法即可忽略null值,避免老數(shù)據(jù)被null覆蓋的尷尬。具體代碼如下:
import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeanWrapperImpl;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import java.util.HashSet;
import java.util.Set;
public class SpringUtil implements ApplicationContextAware {
/**
* 當前IOC
*
*/
private static ApplicationContext applicationContext;
/**
* * 設(shè)置當前上下文環(huán)境,此方法由spring自動裝配
*
*/
@Override
public void setApplicationContext(ApplicationContext arg0)
throws BeansException {
applicationContext = arg0;
}
/**
* 從當前IOC獲取bean
*
* @param id
* bean的id
* @return
*
*/
public static Object getObject(String id) {
Object object = null;
object = applicationContext.getBean(id);
return object;
}
public static String[] getNullPropertyNames (Object source) {
final BeanWrapper src = new BeanWrapperImpl(source);
java.beans.PropertyDescriptor[] pds = src.getPropertyDescriptors();
Set<String> emptyNames = new HashSet<String>();
for(java.beans.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);
}
public static void copyPropertiesIgnoreNull(Object src, Object target){
BeanUtils.copyProperties(src, target, getNullPropertyNames(src));
}
}調(diào)用:copyPropertiesIgnoreNull
public class TestBeanUtiles {
? ? public static void main(String[] args) {
? ? ? ? NewPerson newPerson = new NewPerson();
? ? ? ? newPerson.setName("bifuguo");//前臺用戶更新過的數(shù)據(jù),例如前臺只修改了用戶名
? ? ? ? //下面我們假設(shè)是從數(shù)據(jù)庫加載出來的老數(shù)據(jù)
? ? ? ? OldPerson oldPerson = new OldPerson();
? ? ? ? oldPerson.setSex("nv");
? ? ? ? oldPerson.setAge(5);
? ? ? ? //如果我們想把新數(shù)據(jù)更新到老數(shù)據(jù)這個對象里面,我們就可以借助BeanUtils.copyProperties()的方法如下:
? ? ? ? //BeanUtils.copyProperties(newPerson, oldPerson);
? ? ? ? SpringUtil.copyPropertiesIgnoreNull(newPerson, oldPerson);
? ? ? ? System.out.println(newPerson.toString());
? ? ? ? System.out.println(oldPerson.toString());
? ? }
}打印結(jié)果:
NewPerson{name='bifuguo', sex='null', age=0}
OldPerson{name='bifuguo', sex='nv', age=0}
現(xiàn)在就可以看出老數(shù)據(jù)沒有被null覆蓋
注意點二
1.Spring的BeanUtils的CopyProperties方法需要對應(yīng)的屬性有g(shù)etter和setter方法;
2.如果存在屬性完全相同的內(nèi)部類,但是不是同一個內(nèi)部類,即分別屬于各自的內(nèi)部類,則spring會認為屬性不同,不會copy;
3.泛型只在編譯期起作用,不能依靠泛型來做運行期的限制;
4.最后,spring和apache的copy屬性的方法源和目的參數(shù)的位置正好相反,所以導(dǎo)包和調(diào)用的時候都要注意一下。
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
LinkedBlockingQueue鏈式阻塞隊列的使用和原理解析
這篇文章主要介紹了LinkedBlockingQueue鏈式阻塞隊列的使用和原理解析,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-10-10
SpringBoot JS-SDK自定義微信分享的實現(xiàn)
這篇文章主要介紹了SpringBoot JS-SDK自定義微信分享的實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-09-09
MyBatis-Plus使用ActiveRecord(AR)實現(xiàn)CRUD
本文將結(jié)合實例代碼,介紹MyBatis-Plus使用ActiveRecord(AR)實現(xiàn)CRUD,文中通過示例代碼介紹的非常詳細,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-07-07
詳解Kotlin 高階函數(shù) 與 Lambda 表達式
這篇文章主要介紹了詳解Kotlin 高階函數(shù) 與 Lambda 表達式的相關(guān)資料,需要的朋友可以參考下2017-06-06

