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


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

通常用到的轉換工具類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實現上需要寫的代碼稍微多一點,但是這種方式的性能比通過反射實現更好。下面寫一段代碼來測試兩種方式實現的性能差別。

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。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM