分享公司Entity与DTO之间数据拷贝的方法

主题

  最早以前自学java web的时候,数据库查询出来一个Entity对象(CMP对象).就直接传给前台展示了.并没有用到DTO对象,开始并没有觉得有什么不好...后来发现还是需要一些DTO对象来专门用来传值与前台展示用的.因为直接使用Entity对象有几个地方会比较麻烦:

1.Entity对象的成员域和数据库字段是对应的(比如使用hibernate).所以不能额外向里面增加字段.但是有时候前台就是需要这个字段.反之,前台要向后台传一些值,entity也没办法接受,前台Form表单不可能和数据库公用同一套对象的.

2.有时候完成1个业务需要N个entity.一个一个传给前台很麻烦.这个不是最关键的,最关键的是如果你的service第一次需求定下来的时候只需要1个entity对象做为参数,后来需求变更了,需要用到另外一个entity,这个时候如果修改service的方法签名,增加一个入参,那所有用到这个service的地方都需要修改.而且其他地方不一定有那个业务相关的entity.而方法参数为单独一个DTO的时候就没有这个问题,需要什么其他属性,直接在DTO增加成员域即可.

3.entity可能是由框架控制生命周期的,比如hibernate,如果他的某个属性是懒加载的,那万一不在session中调用的话会抛出异常,而DTO对象很干净,并不会有什么问题.你可以自己控制DTO,喜欢就存在缓存里,不喜欢也没关系.同时entity对象如果属性变化的话会有是否需要同步更新数据库的问题,而DTO与数据库并没有什么卵关系.

 

对象拷贝

所以DTO还是需要的.这样的话就需要entity与dto之间进行数据库的拷贝. 最简单的方法当然是自己调用setget方法..但是显然这样比较麻烦.....因为成员域多的话一个一个set起来很累....

有不少框架都支持对象间属性的拷贝.公司的方法是采用CGLIB的BeanCopier去copy对象,并且在外面稍微包装了一层.

  1 @SuppressWarnings("rawtypes")
  2 public final class ConverterUtil {
  3 
  4     /**
  5      * The Constant LOGGER.
  6      */
  7     private static final Logger LOGGER = LoggerFactory.getLogger(ConverterUtil.class);
  8 
  9     /**
 10      * The Constant CACHED_COPIER_MAP.
 11      */
 12     private static final Map<String, BeanCopier> CACHED_COPIER_MAP = new ConcurrentHashMap<String, BeanCopier>();
 13 
 14     /**
 15      * The Constant CACHED_CUSTOM_CONVERTER_MAP.
 16      */
 17     private static final Map<String, ObjectConverter> CACHED_CUSTOM_CONVERTER_MAP = new ConcurrentHashMap<String, ObjectConverter>();
 18 
 19     /**
 20      * Instantiates a new converter util.
 21      */
 22     private ConverterUtil() {
 23     }
 24 
 25     /**
 26      * Convert list.
 27      * 
 28      * @param <T>
 29      *            the generic type
 30      * @param <F>
 31      *            the generic type
 32      * @param source
 33      *            the source
 34      * @param target
 35      *            the target
 36      * @param sourceList
 37      *            the source list
 38      * @return the list
 39      */
 40     public static <T, F> List<F> convertList(Class<T> source, Class<F> target, List<T> sourceList) {
 41         return convertList(source, target, sourceList, null, null);
 42     }
 43 
 44     /**
 45      * Convert list.
 46      * 
 47      * @param <T>
 48      *            the generic type
 49      * @param <F>
 50      *            the generic type
 51      * @param source
 52      *            the source
 53      * @param target
 54      *            the target
 55      * @param sourceList
 56      *            the source list
 57      * @param converter
 58      *            the converter
 59      * @param customConverterClass
 60      *            the custom converter class
 61      * @return the list
 62      */
 63     public static <T, F> List<F> convertList(Class<T> source, Class<F> target, List<T> sourceList, Converter converter,// NOSONAR
 64             Class<? extends ObjectConverter> customConverterClass) {
 65         if (CollectionUtils.isNotEmpty(sourceList)) {
 66             @SuppressWarnings("unchecked")
 67             List<F> targetList = new ArrayList();
 68             for (T t : sourceList) {
 69                 try {
 70                     F f = target.newInstance();
 71                     targetList.add(convert(t, f, converter, customConverterClass));
 72                 } catch (Exception e) {
 73                     LOGGER.error("When copy instance" + t, e);
 74                 }
 75             }
 76             return targetList;
 77         } else {
 78             return null;// NOSONAR
 79         }
 80 
 81     }
 82 
 83     /**
 84      * Convert.
 85      * 
 86      * @param <T>
 87      *            the generic type
 88      * @param <F>
 89      *            the generic type
 90      * @param source
 91      *            the source
 92      * @param target
 93      *            the target
 94      * @return the f
 95      */
 96     public static <T, F> F convert(T source, F target) {
 97         return convert(source, target, null, null);
 98     }
 99 
100     /**
101      * Convert.
102      * 
103      * @param <T>
104      *            the generic type
105      * @param <F>
106      *            the generic type
107      * @param source
108      *            the source
109      * @param target
110      *            the target
111      * @param converter
112      *            the converter
113      * @param customConverterClass
114      *            the custom converter class
115      * @return the f
116      */
117     public static <T, F> F convert(T source, F target, Converter converter,
118             Class<? extends ObjectConverter> customConverterClass) {
119         if (source == null || target == null) {
120             return null; // NOSONAR
121         }
122         copy(source, target, converter, customConverterClass);
123         return target;
124     }
125 
126     /**
127      * Private methods.
128      * 
129      * @param <T>
130      *            the generic type
131      * @param <F>
132      *            the generic type
133      * @param source
134      *            the source
135      * @param target
136      *            the target
137      * @param converter
138      *            the converter
139      * @param customConverterClass
140      *            the custom converter class
141      */
142 
143     @SuppressWarnings("unchecked")
144     private static <T, F> void copy(T source, F target, Converter converter,
145             Class<? extends ObjectConverter> customConverterClass) {
146         BeanCopier beanCopier = getBeanCopierInstance(source, target.getClass(), converter);
147         beanCopier.copy(source, target, converter);
148         ObjectConverter customConverter = getCustomConverterInstance(customConverterClass);
149         if (customConverter != null) {
150             if (target.getClass().getName().endsWith("CMP")) {
151                 customConverter.convertFromDto(source, target);
152             } else if (target.getClass().getName().endsWith("DTO")) {
153                 customConverter.convertToDto(source, target);
154             }
155         }
156     }
157 
158     /**
159      * Gets the bean copier instance.
160      * 
161      * @param <T>
162      *            the generic type
163      * @param <F>
164      *            the generic type
165      * @param source
166      *            the source
167      * @param targetClass
168      *            the target class
169      * @param converter
170      *            the converter
171      * @return the bean copier instance
172      */
173     private static <T, F> BeanCopier getBeanCopierInstance(T source, Class<F> targetClass, Converter converter) {
174         String key = source.getClass().getName() + "#" + targetClass.getName();
175         BeanCopier beanCopier = CACHED_COPIER_MAP.get(key);
176         if (beanCopier == null) {
177             synchronized (CACHED_COPIER_MAP) {
178                 beanCopier = CACHED_COPIER_MAP.get(key);
179                 if (beanCopier == null) {
180                     beanCopier = TypeAwareBeanCopier.instantiate(source.getClass(), targetClass, converter != null);
181                     CACHED_COPIER_MAP.put(key, beanCopier);
182                 }
183             }
184         }
185         return beanCopier;
186     }
187 
188     /**
189      * Gets the custom converter instance.
190      * 
191      * @param <T>
192      *            the generic type
193      * @param <F>
194      *            the generic type
195      * @param customConverterClass
196      *            the custom converter class
197      * @return the custom converter instance
198      */
199     private static <T, F> ObjectConverter getCustomConverterInstance(
200             Class<? extends ObjectConverter> customConverterClass) {
201         if (customConverterClass == null) {
202             return null;// NOSONAR
203         }
204         String key = customConverterClass.getName();
205         ObjectConverter converter = CACHED_CUSTOM_CONVERTER_MAP.get(key);
206         if (converter == null) {
207             synchronized (CACHED_CUSTOM_CONVERTER_MAP) {
208                 try {
209                     converter = (ObjectConverter) SpringContextUtil.getBean(customConverterClass);
210                 } catch (BeansException e) {// NOSONAR
211                     LOGGER.info(customConverterClass.getName() + " is not a component, need new instance.");// NOSONAR
212                 }
213                 if (converter == null) {
214                     try {
215                         converter = customConverterClass.newInstance();
216                         CACHED_CUSTOM_CONVERTER_MAP.put(key, converter);
217                     } catch (InstantiationException e) {
218                         LOGGER.info(e.getMessage(), e);
219                         return null;
220                     } catch (IllegalAccessException e) {
221                         LOGGER.info(e.getMessage(), e);
222                         return null;
223                     }
224                 }
225             }
226         }
227         return converter;
228     }
229 
230 }

完整的代码就这么多...

convert一个list,其实就是convert了N个普通对象.

convert 1个对象,其实就是new了一个相同类型的对象,然后copy成员域.

所以核心还是copy方法

 1     private static <T, F> void copy(T source, F target, Converter converter,
 2             Class<? extends ObjectConverter> customConverterClass) {
 3         BeanCopier beanCopier = getBeanCopierInstance(source, target.getClass(), converter);
 4         beanCopier.copy(source, target, converter);
 5         ObjectConverter customConverter = getCustomConverterInstance(customConverterClass);
 6         if (customConverter != null) {
 7             if (target.getClass().getName().endsWith("CMP")) {
 8                 customConverter.convertFromDto(source, target);
 9             } else if (target.getClass().getName().endsWith("DTO")) {
10                 customConverter.convertToDto(source, target);
11             }
12         }
13     }

copy方法就2个步骤,第一个步骤是调用CGLIB的BeanCopier的copy方法去copy对象,然后再调用自己写的ObjectConverter接口的实现类去做额外的copy..ObjectConverter接口有2个方法,一个是convertFromDto,就是DTO -> Entity的拷贝,另外一个是convertToDto就是Entity -> Dto或者Dto -> Dto的拷贝.

 

小小的总结

公司的对象间拷贝方法还是比较简单的,同时也蛮好用的,毕竟大多数时候就是收集下前台表单的数据,然后Copy到要保存的entity中,或者数据库查出来Entity,拷贝到前台去展示.

一般String呀,int呀这些都能很方便的拷贝,不过如果成员是List呀,引用对象呀什么的....那就只能自己去实现ObjectConverter接口做额外copy 或者为每个引用对象再掉一次copy方法.

这点是算个小缺陷吧....不过大多数情况下这个简易的转化工具还是超级好用的.

 

posted @ 2016-11-17 11:28 abcwt112 阅读(...) 评论(...) 编辑 收藏