为什么推荐使用Mapstruct取代BeanUtils进行Entity和DTO的转换

选择Mapstruct的核心原因

  1. 代码封装
  2. 速度快(十倍以上的速度优势)

Mapstruct让转换代码聚集在一起

这是一个DTO转Entity的Mapstruct代码,可以看到有如下优势:

  1. 所有赋值工作,都在一个接口里定义,外部进行DTO转Entity时,只需要一句话,极其简洁,无需多行的设置操作。
  2. 可以解决不同字段名的赋值,可以解决赋值需要运行函数获取的方式,也可以解决源为空时,再使用函数赋值等等的常见操作
@Mapper(imports = {IdUtil.class, Date.class, CommonUtil.class, VersionUtil.class, SparringWorkConstant.class, DeleteFlagConstant.class})
public interface LessonDTO2Entity{
    LessonDTO2Entity instance = Mappers.getMapper(LessonDTO2Entity.class);
    
    @Mapping(target="id", source="id", defaultExpression = "java(IdUtil.getId())")
    @Mapping(target="workflowType", source="lessonType")
    @Mapping(target="workflowName", source="lessonName")
    @Mapping(target="workflowDesc", source="lessonIntroduction")
    @Mapping(target="updatedDate", expression = "java(new Date())")
    @Mapping(target="updatedBy", expression = "java(CommonUtil.getCurrentUsername())")
    SparringWorkflowEntity from(AddOrModifyLessonDTO dto);
    
    /*
    from dto
     */
    @Mapping(target="id", source="id", defaultExpression = "java(IdUtil.getId())")
    @Mapping(target="workflowType", source="lessonType")
    @Mapping(target="workflowName", source="lessonName")
    @Mapping(target="workflowDesc", source="lessonIntroduction")
    /*
    init values
     */
    @Mapping(target="workflowCode", expression = "java(IdUtil.uuid())")
    @Mapping(target="createdDate", expression = "java(new Date())")
    @Mapping(target="updatedDate", expression = "java(new Date())")
    @Mapping(target="createdBy", expression = "java(CommonUtil.getCurrentUsername())")
    @Mapping(target="updatedBy", expression = "java(CommonUtil.getCurrentUsername())")
    @Mapping(target="workflowVersion", expression = "java(VersionUtil.init())")
    @Mapping(target="workflowState", expression = "java(SparringWorkConstant.EXECUTABLE_STATE)")
    @Mapping(target="deleteFlag", expression = "java(DeleteFlagConstant.persist)")
    SparringWorkflowEntity fromAndInit(AddOrModifyLessonDTO dto);

}

Mapstruct速度非常快

性能比较代码如下:

@Test
void performance() {

    AddOrModifyLessonDTO dto = new AddOrModifyLessonDTO();
    dto.setId("123");
    dto.setLessonType("dialogue");
    dto.setLessonName("test lesson");
    dto.setLessonIntroduction("test lesson description");

    SparringWorkflowEntity sparringWorkflowEntity = null;

    final long LOOP_TIMES = 100000;

    StopWatch stopWatch = new StopWatch();

    stopWatch.start("mapstruct copy dto");
    for(int i=0;i<LOOP_TIMES;i++){
        sparringWorkflowEntity = lessonDTO2Entity.instance.from(dto);
    }
    stopWatch.stop();

    stopWatch.start("beansutils copy dto");
    for(int i=0;i<LOOP_TIMES;i++){
        BeanUtils.copyProperties(dto, sparringWorkflowEntity);
        sparringWorkflowEntity.setWorkflowName(dto.getLessonName());
        sparringWorkflowEntity.setWorkflowDesc(dto.getLessonIntroduction());
        sparringWorkflowEntity.setWorkflowType(dto.getLessonType());
        sparringWorkflowEntity.setUpdatedDate(new Date());
        sparringWorkflowEntity.setUpdatedBy(CommonUtil.getCurrentUsername());
    }
    stopWatch.stop();

    log.info(stopWatch.prettyPrint());
}

测试结果:

---------------------------------------------

ns         %     Task name

---------------------------------------------

032059349  005%  mapstruct copy dto

555997343  095%  beansutils copy dto

mapstruct 比 beansutils的方式快了 17 倍

原因分析:

mapstruct在代码编译阶段,就将接口的Map逻辑转换为set方法,为原生代码实现;

而beansutils是基于java的反射机制实现,通过反射获取类变量是比较慢的;

DTO和Entity的java对象转换,根据典型场景罗列如下FAQ

Q:如果DTO和Entity的字段名和类型相同

A:不需要处理,默认赋值

Q:字段名不同

A: 通过在函数上加Mapping清晰描述映射关系

@Mapping(target="workflowName", source="lessonName")

Q:值是执行函数得到的且没有入参,如 updatedBy, createdDate

A: 通过在函数上加

@Mapping(target="updatedDate", expression = "java(new Date())"

默认需要写全路径,上述可以简写,是因为

在类上加入了imports,能明确知道Date来自这个Date.class

@Mapper(imports = {IdUtil.class, Date.class})

Q:值是执行函数得到,且有入参

A: 通过qualifiedByName

@Mapping(target="workflowType", source="lessonType", qualifiedByName="LessonTypeEnum2WorkflowType")
@Named("LessonTypeEnum2WorkflowType")
public static String LessonTypeEnum2WorkflowType(ESparringWorkflowType lessonType){
    return lessonType == null? null : lessonType.getCode();
}

Q:列表的赋值

A: 先定义一个列表转换函数,然后对列表内部的元素的赋值再定义一个函数,具体的规则在元素的函数上定义

@Mapping(source="workflowType", target="lessonType", qualifiedByName = "WorkflowType2LessonTypeEnum")
@Mapping(source="workflowName", target="lessonName")
GetOrModifyLessonResponseDTO to(SparringWorkflow sparringWorkflow);

List<GetLessonsResponseDTO> to(List<SparringWorkflow> sparringWorkflow);

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值