为啥建议用MapperStruct,不建议用BeanUtils.copyProperties拷贝数据?

为啥建议用MapperStruct,不建议用BeanUtils.copyProperties拷贝数据?

在实际的业务开发中,我们经常会碰到BO、PO、DTO等对象属性之间的赋值 ,当属性较多的时候我们使用get,set的方式进行赋值的工作量相对较大 ,因此很多人会选择使用spring提供的拷贝工具BeanUtils的copyProperties方法完成对象之间属性的拷贝。通过这种方式可以很大程度上降低我们手动编写对象属性赋值代码的工作量,既然它那么方便为什么还不建议使用呢?

我总结为以下几点:

  • 属性类型不一致导致拷贝失败

    在实际开发中,很可能会出现同一字段在不同的类中定义的类型不一致,例如Id,可能在A类中定义的类型为Long,在B类中定义的类型为String,此时如果使用BeanUtils.copyProperties进行拷贝,就会出现拷贝失败的现象,导致对应的字段为null

  • 同一字段分别使用包装类型和基本类型

    如果同一个字段分别使用包装类和基本类型,在没有传递实际值的时候,会出现异常

    注意,如果一个布尔类型的属性分别使用了基本类型和包装类型,且属性名如果使用is开头,例如isSuccess,也会导致拷贝失败

  • null值覆盖导致数据异常

    在业务开发时,我们可能会有部分字段拷贝的需求,被拷贝的数据里面如果某些字段有null值存在,但是对应的需要被拷贝过去的数据的相同字段的值并不为null,如果直接使用 BeanUtils.copyProperties 进行数据拷贝,就会出现被拷贝数据的null值覆盖拷贝目标数据的字段,导致原有的数据失效

    简单举个例子,我有一个BO(已经存在了A属性且有值),需要拷贝DTO中除了A属性的其他属性(DTO中的A属性为null),此时使用BeanUtils.copyProperties就会导致BO中的A属性的值被DTO中A的null给覆盖了

    虽然可以使用 BeanUtils.copyProperties 的重载方法,配合自定义的 ConvertUtilsBean 来实现部分字段的拷贝,但是这么做本身也比较复杂,也就失去了使用BeanUtils.copyProperties 拷贝数据的意义,因此也不推荐这么做。

  • 底层实现为反射拷贝效率低

  • 导包错误导致拷贝数据异常

具体更加详细的可以去看这篇文章 :为啥不建议用BeanUtils.copyProperties拷贝数据 | 京东云技术团队 - 掘金 (juejin.cn)

为什么使用MapperStruct

为什么使用MapperStruct? ---> 快,对就是因为很快,性能比BeanUtils.copyProperties快很多很多

放张参考图给大家参考

那么为什么MapperStruct比BeanUtils.copyProperties快?

  • 避免了反射操作的性能开销
  • 预编译的高效代码执行更快 (看到这里就懵了,预编译?咋回事啊到底)

什么是预编译? --- 在项目启动的时候,自动帮你生成对象拷贝的代码,拒绝使用的时候才去通过反射调用get/set...

具体的可以往下看

java 复制代码
@Generated(
    value = "org.mapstruct.ap.MappingProcessor",
    date = "2024-02-07T00:50:05+0800",
    comments = "version: 1.4.2.Final, compiler: javac, environment: Java 1.8.0_211 (Oracle Corporation)"
)
public class XXXDTOConverterImpl implements XXXDTOConverter {

    @Override
    public XXXBO convertDtoToBo(XXXDTO XXXDTO) {
        if ( XXXDTO == null ) {
            return null;
        }

        XXXBO XXXBO = new XXXBO();

        XXXBO.setId( XXXDTO.getId() );
        XXXBO.setLabelName( XXXDTO.getLabelName() );
        XXXBO.setSortNum( XXXDTO.getSortNum() );
        XXXBO.setCategoryId( XXXDTO.getCategoryId() );

        return XXXBO;
    }
}

怎么使用MapperStruct

Maven引入依赖

xml 复制代码
        <dependency>
            <groupId>org.mapstruct</groupId>
            <artifactId>mapstruct</artifactId>
            <version>1.4.2.Final</version>
        </dependency>
        <dependency>
            <groupId>org.mapstruct</groupId>
            <artifactId>mapstruct-processor</artifactId>
            <version>1.4.2.Final</version>
        </dependency>

编写代码

java 复制代码
@Data
public class SubjectLabelDTO implements Serializable {
    private String labelName;
}
@Data
public class SubjectLabelBO implements Serializable {
    private String labelName;
}
java 复制代码
@Mapper
public interface SubjectLabelDTOConverter {

    SubjectLabelDTOConverter INSTANCE = Mappers.getMapper(SubjectLabelDTOConverter.class);

    SubjectLabelBO convertDtoToBo(SubjectLabelDTO subjectLabelDTO);
}
java 复制代码
public Result<Boolean> add( SubjectLabelDTO subjectLabelDTO){
        SubjectLabelBO subjectLabelBO = SubjectLabelDTOConverter.INSTANCE
        									.convertDtoToBo(subjectLabelDTO);
}

总结

Mapper-struct同样也是浅拷贝,需要进行深拷贝,就写多个Converter把需要深拷贝的再转一次吧,如果要配置深拷贝

太麻烦了

相关推荐
It's now3 分钟前
Spring AI 基础开发流程
java·人工智能·后端·spring
cxh_陈3 分钟前
线程的状态,以及和锁有什么关系
java·线程·线程的状态·线程和锁
计算机毕设VX:Fegn08956 分钟前
计算机毕业设计|基于springboot + vue图书商城系统(源码+数据库+文档)
java·数据库·vue.js·spring boot·后端·课程设计
R.lin18 分钟前
Java 8日期时间API完全指南
java·开发语言·python
毕设源码-赖学姐24 分钟前
【开题答辩全过程】以 高校教学质量监控平台为例,包含答辩的问题和答案
java·eclipse
高山上有一只小老虎32 分钟前
翻之矩阵中的行
java·算法
火钳游侠1 小时前
java单行注释,多行注释,文档注释
java·开发语言
code bean1 小时前
【CMake】为什么需要清理 CMake 缓存文件?深入理解 CMake 生成器切换机制
java·spring·缓存
selt7911 小时前
Redisson之RedissonLock源码完全解析
android·java·javascript
RestCloud1 小时前
智能制造的底层基建:iPaaS 如何统一 ERP、MES 与 WMS 的数据流
java·wms·erp·数据传输·ipaas·mes·集成平台