通常状况下,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。性能