【Spring】BeanUtils.copyPorperties()的IllegalArgumentException原因分析

Jiadong發表於2017-07-04
  • 前置知識: SpringBean ORM Java企業級開發基礎

背景

在使用ORM框架讀取資料庫表記錄時,為了把PO(Persist Object)轉換成BO(Business Object),由於PO和BO中的欄位絕大多數情況下高度重合,因此copyProperties()也是經常使用的函式,但是如果使用不當就會丟擲Exception

舉個例子,有這麼一個系統:

  1. Database的Table中有data欄位(tinyint)
  2. PO中有data欄位(Boolean)
  3. BO中有data欄位(boolean)

在資料庫的data欄位為null時,呼叫copyProperties(PO,BO)時就會丟擲異常:Caused by java.lang.IllegalArgumentException

程式碼分析

Example of copyProperties()

private static void copyProperties(Object source, Object target, Class<?> editable, String[] ignoreProperties)
            throws BeansException {
/** 略 **/
                if (sourcePd != null && sourcePd.getReadMethod() != null) {
                    try {
                        Method readMethod = sourcePd.getReadMethod();
                        if (!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers())) {
                            readMethod.setAccessible(true);
                        }
                        Object value = readMethod.invoke(source);
                        Method writeMethod = targetPd.getWriteMethod();
                        if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers())) {
                            writeMethod.setAccessible(true);
                        }
                        writeMethod.invoke(target, value); /**異常丟擲點**/
                    }
                    catch (Throwable ex) {
                        throw new FatalBeanException("Could not copy properties from source to target", ex);
                    }
                }
    /** 略 **/
}

總結一下: 該方法複製欄位(可以不同Class,但是目標欄位的型別必須和源欄位型別相容)原理是獲得源物件欄位的getter方法和目標物件欄位的setter方法

Example of PO and its ReadMethod

private Boolean data;
public Boolean getData(Boolean data){
   return this.data;
}

Example of BO and its WriteMethod

private boolean data;
public setData(boolean data){
    this.data = data;
}

具體就是掛在呼叫BO.setData(null)時, 對一個基本型別boolean賦值為null

措施分析

  1. 新增資料庫欄位時指定預設值,並設定為Not Null
  2. 為PO的欄位指定預設值,如private Boolean data = true;

    • _推薦這種方式_,因為BO中欄位為基本型別,上面的業務層就不需要額外判斷是否是null
    • 如果表中資料為null,則ORM(iBatis/MyBatis)不會呼叫PO相應欄位的setter方法,所以為PO的欄位指定預設值是可行的

相關文章