分享公司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对象,并且在外面稍微包装了一层.
@SuppressWarnings("rawtypes")
public final class ConverterUtil {
/**
* The Constant LOGGER.
*/
private static final Logger LOGGER = LoggerFactory.getLogger(ConverterUtil.class);
/**
* The Constant CACHED_COPIER_MAP.
*/
private static final Map<String, BeanCopier> CACHED_COPIER_MAP = new ConcurrentHashMap<String, BeanCopier>();
/**
* The Constant CACHED_CUSTOM_CONVERTER_MAP.
*/
private static final Map<String, ObjectConverter> CACHED_CUSTOM_CONVERTER_MAP = new ConcurrentHashMap<String, ObjectConverter>();
/**
* Instantiates a new converter util.
*/
private ConverterUtil() {
}
/**
* Convert list.
*
* @param <T>
* the generic type
* @param <F>
* the generic type
* @param source
* the source
* @param target
* the target
* @param sourceList
* the source list
* @return the list
*/
public static <T, F> List<F> convertList(Class<T> source, Class<F> target, List<T> sourceList) {
return convertList(source, target, sourceList, null, null);
}
/**
* Convert list.
*
* @param <T>
* the generic type
* @param <F>
* the generic type
* @param source
* the source
* @param target
* the target
* @param sourceList
* the source list
* @param converter
* the converter
* @param customConverterClass
* the custom converter class
* @return the list
*/
public static <T, F> List<F> convertList(Class<T> source, Class<F> target, List<T> sourceList, Converter converter,// NOSONAR
Class<? extends ObjectConverter> customConverterClass) {
if (CollectionUtils.isNotEmpty(sourceList)) {
@SuppressWarnings("unchecked")
List<F> targetList = new ArrayList();
for (T t : sourceList) {
try {
F f = target.newInstance();
targetList.add(convert(t, f, converter, customConverterClass));
} catch (Exception e) {
LOGGER.error("When copy instance" + t, e);
}
}
return targetList;
} else {
return null;// NOSONAR
}
}
/**
* Convert.
*
* @param <T>
* the generic type
* @param <F>
* the generic type
* @param source
* the source
* @param target
* the target
* @return the f
*/
public static <T, F> F convert(T source, F target) {
return convert(source, target, null, null);
}
/**
* Convert.
*
* @param <T>
* the generic type
* @param <F>
* the generic type
* @param source
* the source
* @param target
* the target
* @param converter
* the converter
* @param customConverterClass
* the custom converter class
* @return the f
*/
public static <T, F> F convert(T source, F target, Converter converter,
Class<? extends ObjectConverter> customConverterClass) {
if (source == null || target == null) {
return null; // NOSONAR
}
copy(source, target, converter, customConverterClass);
return target;
}
/**
* Private methods.
*
* @param <T>
* the generic type
* @param <F>
* the generic type
* @param source
* the source
* @param target
* the target
* @param converter
* the converter
* @param customConverterClass
* the custom converter class
*/
@SuppressWarnings("unchecked")
private static <T, F> void copy(T source, F target, Converter converter,
Class<? extends ObjectConverter> customConverterClass) {
BeanCopier beanCopier = getBeanCopierInstance(source, target.getClass(), converter);
beanCopier.copy(source, target, converter);
ObjectConverter customConverter = getCustomConverterInstance(customConverterClass);
if (customConverter != null) {
if (target.getClass().getName().endsWith("CMP")) {
customConverter.convertFromDto(source, target);
} else if (target.getClass().getName().endsWith("DTO")) {
customConverter.convertToDto(source, target);
}
}
}
/**
* Gets the bean copier instance.
*
* @param <T>
* the generic type
* @param <F>
* the generic type
* @param source
* the source
* @param targetClass
* the target class
* @param converter
* the converter
* @return the bean copier instance
*/
private static <T, F> BeanCopier getBeanCopierInstance(T source, Class<F> targetClass, Converter converter) {
String key = source.getClass().getName() + "#" + targetClass.getName();
BeanCopier beanCopier = CACHED_COPIER_MAP.get(key);
if (beanCopier == null) {
synchronized (CACHED_COPIER_MAP) {
beanCopier = CACHED_COPIER_MAP.get(key);
if (beanCopier == null) {
beanCopier = TypeAwareBeanCopier.instantiate(source.getClass(), targetClass, converter != null);
CACHED_COPIER_MAP.put(key, beanCopier);
}
}
}
return beanCopier;
}
/**
* Gets the custom converter instance.
*
* @param <T>
* the generic type
* @param <F>
* the generic type
* @param customConverterClass
* the custom converter class
* @return the custom converter instance
*/
private static <T, F> ObjectConverter getCustomConverterInstance(
Class<? extends ObjectConverter> customConverterClass) {
if (customConverterClass == null) {
return null;// NOSONAR
}
String key = customConverterClass.getName();
ObjectConverter converter = CACHED_CUSTOM_CONVERTER_MAP.get(key);
if (converter == null) {
synchronized (CACHED_CUSTOM_CONVERTER_MAP) {
try {
converter = (ObjectConverter) SpringContextUtil.getBean(customConverterClass);
} catch (BeansException e) {// NOSONAR
LOGGER.info(customConverterClass.getName() + " is not a component, need new instance.");// NOSONAR
}
if (converter == null) {
try {
converter = customConverterClass.newInstance();
CACHED_CUSTOM_CONVERTER_MAP.put(key, converter);
} catch (InstantiationException e) {
LOGGER.info(e.getMessage(), e);
return null;
} catch (IllegalAccessException e) {
LOGGER.info(e.getMessage(), e);
return null;
}
}
}
}
return converter;
}
}
完整的代码就这么多...
convert一个list,其实就是convert了N个普通对象.
convert 1个对象,其实就是new了一个相同类型的对象,然后copy成员域.
所以核心还是copy方法
private static <T, F> void copy(T source, F target, Converter converter,
Class<? extends ObjectConverter> customConverterClass) {
BeanCopier beanCopier = getBeanCopierInstance(source, target.getClass(), converter);
beanCopier.copy(source, target, converter);
ObjectConverter customConverter = getCustomConverterInstance(customConverterClass);
if (customConverter != null) {
if (target.getClass().getName().endsWith("CMP")) {
customConverter.convertFromDto(source, target);
} else if (target.getClass().getName().endsWith("DTO")) {
customConverter.convertToDto(source, target);
}
}
}
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方法.
这点是算个小缺陷吧....不过大多数情况下这个简易的转化工具还是超级好用的.
分享公司Entity与DTO之间数据拷贝的方法的更多相关文章
- DistCp 集群之间数据拷贝工具
DistCp(分布式拷贝)是用于大规模集群内部和集群之间拷贝的工具.可以将数据拷贝到另个一集群,也可以将另一个集群的数据拷贝到本集群.
- JAVA中JavaBean对象之间属性拷贝的方法
JAVA中JavaBean对象之间的拷贝通常是用get/set方法,但如果你有两个属性相同的JavaBean或有大部分属性相同的JavaBean,对于这种情况,可以采用以下几个简便方法处理. 下面对这 ...
- 何时使用Entity或DTO
关注公众号: 锅外的大佬 每日推送国外优秀的技术翻译文章,励志帮助国内的开发者更好地成长! JPA和Hibernate允许你在JPQL和Criteria查询中使用DTO和Entity作为映射.当我在我 ...
- 解析activity之间数据传递方法的详解
转自:http://www.jb51.net/article/37227.htm 本篇文章是对activity之间数据传递的方法进行了详细的分析介绍,需要的朋友参考下 1 基于消息的通信机制 ...
- 软件架构自学笔记----分享“去哪儿 Hadoop 集群 Federation 数据拷贝优化”
去哪儿 Hadoop 集群 Federation 数据拷贝优化 背景 去哪儿 Hadoop 集群随着去哪儿网的发展一直在优化改进,基本保证了业务数据存储量和计算量爆发式增长下的存储服务质量.然而,随着 ...
- MySQL与Oracle之间互相拷贝数据的Java程序
因为工作需要,先是需要将一个小型的MySQL数据库中的数据拷贝到Oracle中:近期又有需要将一个中型的Oracle数据库拷贝到MySQL中.曾经找过网上各种转换工具,大多收费的,自己写个吧,还一切可 ...
- JS之document例题讲解1(两张表之间数据转移、日期时间选择、子菜单下拉、用div做下拉菜单、事件总结)
作业一:两个列表之间数据从一个列表移动到另一个列表 <div style="width:600px; height:500px; margin-top:20px"> & ...
- python之 《进程之间数据交互和进程池》
1.进程q 进程呢就相当于一个房子,线程就相当于是房子里面在工作的人,那么一个房子的空间对于房子里面的人来说是共享的, 现在是多进程,也就是说有许多房子,很显然这个房子的空间只属于这个房子,不会属于其 ...
- DOM的小练习,两个表格之间数据的移动
本次讲的是两个表格之间数据的移动,左边的表格移动到右边,并且左边表格移动内容消失. <head> <meta http-equiv="Content-Type" ...
随机推荐
- Oracle学习笔记六 SQL常用函数
函数的分类 Oracle 提供一系列用于执行特定操作的函数 SQL 函数带有一个或多个参数并返回一个值 以下是SQL函数的分类:
- linux shell字符串内置的常用操作(获取长度、查找、替换)
在编写shell程序时,经常会涉及到字符串相关操作.有许多命令语句,如awk,sed都能够做字符串各种操作. 事实上shell内置一系列操作符号,能够达到相似效果,使用内部操作符会省略启动外部程序等时 ...
- [转]ASP.NET Core 中的那些认证中间件及一些重要知识点
本文转自:http://www.qingruanit.net/c_all/article_6645.html 在读这篇文章之间,建议先看一下我的 ASP.NET Core 之 Identity 入门系 ...
- git submodule初用
git submodule主要是用于针对git项目中还存在git子模块的情况.在一般情况下,我们通过git clone 获取项目的时候会把项目中的所有信息都拿到.但是,如果相关中存在git子模块那么, ...
- python flask (一)
from flask import Flask app = Flask(__name__) @app.route('/') def hello_world(): return 'Hello World ...
- phpmyadmin导入数据库大小限制修改
phpmyadmin默认导入数据库文件大小为2M,但一般网站的数据库导出的文件都会超出这个限制,要导入超过2M的数据库文件就需要手动修改php.ini配置文件! 在php.ini文件中修改: uplo ...
- The Euler function[HDU2824]
The Euler functionTime Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)To ...
- [LeetCode] Arithmetic Slices II - Subsequence 算数切片之二 - 子序列
A sequence of numbers is called arithmetic if it consists of at least three elements and if the diff ...
- 我们都是IT民工---------流浪人IDE开发札记
你生命中的有些东西终究会失去,比如我住了6年的陈寨,这个聚集了郑州十几万IT民工的地方,说拆就拆了.再比如我玩了3年的坦克英雄,这个带给我太多快乐的游戏,说停就停了. 编程对我而言是种爱好,我上学6年 ...
- css3圣诞雪景球
本来想多做几个了 无奈最近太忙 于是模仿做了一个在codepen看到的圣诞雪景球 算是送给自己的圣诞礼物 演示地址:http://www.qdfuns.com/notes/26668/d5e177 ...