DO-DTO相互轉換時的性能優化

通常狀況下,DO是用來映射數據庫記錄的實體類,DTO是用來在網絡上傳輸的實體類。二者的不一樣除了適用場景不一樣外還有就是DTO須要實現序列化接口。從DB查詢到數據以後,ORM框架會把數據轉換成DO對象,一般咱們須要再把DO對象轉換爲DTO對象。一樣的,插入數據到DB以前須要將DTO對象轉換爲DO對象而後交給ORM框架去執行JDBC。java

一般用到的轉換工具類BeanUtils是經過反射來實現的,實現源碼以下數據庫

public static <T> T convertObject(Object sourceObj, Class<T> targetClz) {
    if (sourceObj == null) {
        return null;
    }
    if (targetClz == null) {
        throw new IllegalArgumentException("parameter clz shoud not be null");
    }
    try {
        Object targetObj = targetClz.newInstance();
        BeanUtils.copyProperties(sourceObj, targetObj);
        return (T) targetObj;
    } catch (Exception e) {
        throw new RuntimeException(e);
    }
}

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;

    for (PropertyDescriptor targetPd : targetPds) {
        if (targetPd.getWriteMethod() != null &&
                (ignoreProperties == null || (!ignoreList.contains(targetPd.getName())))) {
            PropertyDescriptor sourcePd = getPropertyDescriptor(source.getClass(), targetPd.getName());
            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);
                }
            }
        }
    }
}

也能夠經過mapstruct來實現,這種方式是在Mapper接口的包中生成一個對應mapper的實現類,實現類的源碼以下。顯然這種方式的實現更爲普通,看起來沒有BeanUtils的實現那麼複雜。不過BeanUtils經過反射實現更爲通用,能夠爲各類類型的DTO實現轉換。而mapstruct只是幫咱們生產了咱們不想寫的代碼。網絡

public Task doToDTO(TaskDO taskDO) {
        if (taskDO == null) {
            return null;
        } else {
            Task task = new Task();
            task.setId(taskDO.getId());
            //其餘字段的set
            task.setGmtCreate(taskDO.getGmtCreate());
            task.setGmtModified(taskDO.getGmtModified());
            return task;
        }
    }

對比以上兩種方式,顯然使用BeanUtils來進行轉換時須要寫的代碼更少,內部的經過反射即可以進行get/set操做。而mapstruct實現上須要寫的代碼稍微多一點,可是這種方式的性能比經過反射實現更好。下面寫一段代碼來測試兩種方式實現的性能差異。app

public void testConvert() {
    System.out.println("####testConvert");
    int num = 100000;
    TaskDO taskDO = new TaskDO();
    long start = System.currentTimeMillis();
    for (int i = 0; i < num; i ++) {
        Task task = ObjectConvertor.convertObject(taskDO, Task.class);
    }
    System.out.println(System.currentTimeMillis() - start);
    //---------------------------------------------
    start = System.currentTimeMillis();
    for (int i = 0; i < num; i ++) {
        Task task = taskMapper.doToDTO(taskDO);
    }
    System.out.println(System.currentTimeMillis() - start);
}

以上測試代碼中分別使用兩種方式對同一個DO對象進行n次轉換,兩次轉換的耗時統計以下。單位:ms框架

次數 1 10 100 1000 10000 100000 1000000 10000000
Mapstruct 0 1 1 1 2 4 8 8
BeanUtil 9 7 11 26 114 500 1469 14586

可見當轉換數量級增長時,使用BeanUtil的耗時急劇上升,而使用Mapstruct的耗時則保持在比較低的水平。工具

在一個系統中,ORM對DB的各類操做幾乎都會涉及到DO和DTO之間的轉換,參考以上表格的統計結果,更推薦使用Mapstruct。性能

相關文章
相關標籤/搜索