分享公司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方法.

這點是算個小缺陷吧....不過大多數情況下這個簡易的轉化工具還是超級好用的.

 


免責聲明!

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



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