java中的bo和do_java中BO,VO,DTO,DO等之间的转换

文章目录

1 介绍

2 转换

2.1 通过get,set方法进行类型转换

2.2 基于反射的机制

2.3 基于cglib字节码修改

1 介绍

阿里的java代码开发规范中,关于各个层的命名规范是这么定义

这里面有我们比较常见的POJO对应的实体定义格式,其中

VO(View Object):是针对于视图层,用于展示层(前端页面),它的作用是把某个视图需要展示的数据进行封装。

DTO(Data Transfer Object):数据传输对象,这个概念来源于J2EE的设计模式,原来的目的是为了EJB的分布式应用提供粗粒度的数据实体,以减少分布式调用的次数,从而提高分布式调用的性能和降低网络负载,但在这里,泛指用于展示层与服务层之间的数据传输对象。 其中以Query后缀,有时候也可以作为DTO的一部分。

DO(Domain Object):领域对象,就是从现实世界中抽象出来的有形或无形的业务实体。

PO(Persistent Object):持久化对象,它跟持久层(通常是关系型数据库)的数据结构形成一一对应的映射关系,如果持久层是关系型数据库,那么,数据表中的每个字段(或若干个)就对应PO的一个(或若干个)属性。

2 转换

常见的业务中,我们都是会将由视图层(View)的数据封装成对应的DTO,传递给对应的服务接口,服务接口在接收到对应的DTO数据之后,进行参数校验,比如空值判断,类型匹配,初始化值操作等。

接着,我们会将验证过后的DTO数据,交由Service层进行数据操作(或者是分布式场景下的RPC远程调度接口),Service层通过与之关联的DO业务数据进行转换之后,会形成一个新的DTO数据,再交由DAO层数据进行数据持久化

在这个业务场景下,一般的,我们都是会涉及到对应的BO,VO,DTO,DO等POJO类之间的转换问题。

下面就来简单的分析一下

2.1 通过get,set方法进行类型转换

这个是最原始,最直接的转换方式。比如视图层发起了一个商品入库的交易,对应的DTO如下:

@Data

public class GoodsDTO {

/**

* 商品价格

*/

private Double price;

/**

* 商品名称

*/

private String name;

/**

* 商品数量

*/

private Integer num;

}

通过Service层的入库时间确定,入库人确定等操作,最终形成的PO如下:

@Data

public class GoodsPO {

/**

* 商品价格

*/

private Double price;

/**

* 商品名称

*/

private String name;

/**

* 商品数量

*/

private Integer num;

/**

* 入库时间

*/

private Date createTime;

/**

* 操作人

*/

private String creatBy;

}

通过get set 方法

// 1,验证商品

check(goodsDTO);

GoodsPO goodsPO = new GoodsPO();

goodsPO.setName(goodsDTO.getName());

goodsPO.setNum(goodsDTO.getNum());

goodsPO.setPrice(goodsDTO.getPrice());

// 2,追加入库时间,入库人操作

goodsPO.setCreatBy("admin");

goodsPO.setCreateTime(new Date());

get set 的方式进行转换的方式很简单,但是有一个很验证的问题就是会导致代码过于臃肿,会出现大量的属性值赋值,大量的get set方法充斥着业务逻辑,

2.2 基于反射的机制

我们可以基于java反射的机制进行对象之间的属性值的复制操作。我们可以创建一个抽象的模型转换器,AbstractModelConverter

/**

* POJO 转换器

*

* @author zhoucg

* @date 2019-12-25 16:37

*/

public abstract class AbstractModelConverter{

/**

* 转换

* @param clazz the class of convert object

* @return to convert object

*/

public T toPo(Classclazz) {

MapconvertFileMapper = convert();

T t = Func.copy(this, clazz, CopyOptions.create().setFieldMapping(convertFileMapper));

return t;

}

/**

* 由子类重写该方法,作为ModelConvert->T 的属性映射关系

* @return fieldMapper

*/

public abstract Mapconvert();

}

针对于相同或者不同的属性值,我们可以做两个POJO之间的属性映射Mapper,通过Map的方式存储,再交由反射进行属性填充。

字节码属性映射示例:

public static Object convert(Object convertOld,ClassconvertNew,Listexcludes,MapfieldMap) {

Field[] oldFields = convertOld.getClass().getDeclaredFields();

Field[] newFields = convertNew.getDeclaredFields();

Object newProxyObject = null;

try {

newProxyObject = convertNew.newInstance();

} catch (InstantiationException e) {

logger.error("初始化对象错误,当前错误信息:{}",e.getMessage());

} catch (IllegalAccessException e) {

logger.error("class类无对应的访问权限,当前错误信息:{}",e.getMessage());

}

MapnewFieldsMap = Maps.newHashMap();

Arrays.stream(newFields).forEach(localfiled -> newFieldsMap.put(localfiled.getName(),localfiled));

for(Field field : oldFields) {

String fieldName = field.getName();

String fieldMapperValue;

//找到新的映射关系

if(fieldMap == null || fieldMap.size() == 0) {

fieldMapperValue = fieldName;

} else {

if(fieldMap.containsKey(fieldName)) {

fieldMapperValue = fieldMap.get(fieldName);

} else {

fieldMapperValue = fieldName;

}

}

if(excludes != null && excludes.contains(fieldName)) continue;

if(newFieldsMap.keySet().contains(fieldMapperValue)) {

try {

Field relationNewField = newFieldsMap.get(fieldMapperValue);

relationNewField.setAccessible(true);

field.setAccessible(true);

relationNewField.set(newProxyObject,field.get(convertOld));

} catch (IllegalAccessException e) {

logger.error("反射ClassFiled错误,当前错误信息:{}",e.getMessage());

}

}

}

return newProxyObject;

}

在Spring的源码中,BeanUtils已经封装了大量的对象转换属性复制的方法,我们可以直接进行使用

2.3 基于cglib字节码修改

BeanCopier是cglib包下的一个类。它可以通过直接修改字节码的方式进行属性值间的复制

默认的情况下,它会进行同名,同类型属性的copier,如果类型不同,或者名称不同的化,会报错,我们可以通过实现自定义Convert的形式进行类型转换

BeanCopier copier = BeanCopier.create(goodsDTO.getClass(), goodsPO.getClass(), false);

假设在GoodsDTO中存在一个Date 类型的插入时间(insertTime),我们需要转换成对应字符类型的插入时间(insertTime)

BeanCopier copier = BeanCopier.create(goodsDTO.getClass(), goodsPO.getClass(), true);

copier.copy(goodsDTO, goodsPO, (value, target, context) -> {

return value.getClass().isAssignableFrom(Date.class) ? new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(value) : value;

});

BeanCopier 源码见Sping中spring-core

对应性能而言,最差的肯定是基于反射机制的方法,get /set是性能最好。

所以我们需要权衡代码的整洁性和性能,

个人不太建议使用cglib的BeanCopier ,针对于不同属性名的转换,BeanCopier 是无法完成的。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值