当前位置: 欣欣网 > 码农

强烈推荐 MapperStruct,不建议用 BeanUtils.copyProperties 拷贝数据?

2024-05-29码农

来源:juejin.cn/post/7333458486435987494

👉 欢迎 ,你将获得: 专属的项目实战 / Java 学习路线 / 一对一提问 / 学习打卡 / 每月赠书

新项目: 仿小红书 (微服务架构)正在更新中... , 全栈前后端分离博客项目 2.0 版本完结啦, 演示链接 http://116.62.199.48/ 。全程手摸手,后端 + 前端全栈开发,从 0 到 1 讲解每个功能点开发步骤,1v1 答疑,直到项目上线。 目前已更新了261小节,累计43w+字,讲解图:1806张,还在持续爆肝中.. 后续还会上新更多项目,目标是将Java领域典型的项目都整一波,如秒杀系统, 在线商城, IM即时通讯,Spring Cloud Alibaba 等等,

  • 为什么使用MapperStruct

  • 怎么使用MapperStruct

  • 总结

  • 在实际的业务开发中,我们经常会碰到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 拷贝数据的意义,因此也不推荐这么做。

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

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

  • 为什么使用MapperStruct

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

    放张参考图给大家参考

    图片

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

  • 避免了反射操作的性能开销

  • 预编译的高效代码执行更快 (看到这里就懵了,预编译?咋回事啊到底)

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

    具体的可以往下看

    @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引入依赖

    <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>

    编写代码

    @Data
    public class SubjectLabelDTO implements Serializable {
    private String labelName;
    }
    @Data
    public class SubjectLabelBO implements Serializable {
    private String labelName;
    }
    @Mapper
    public interface SubjectLabelDTOConverter {
    SubjectLabelDTOConverter INSTANCE = Mappers.getMapper(SubjectLabelDTOConverter. class);
    SubjectLabelBO convertDtoToBo(SubjectLabelDTO subjectLabelDTO);
    }
    public Result<Boolean> add( SubjectLabelDTO subjectLabelDTO){
    SubjectLabelBO subjectLabelBO = SubjectLabelDTOConverter.INSTANCE
    .convertDtoToBo(subjectLabelDTO);
    }

    总结

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

    Mapper-struct也存在一点小问题,跟lombok在配合的时候会出现问题,如果mapperStruct的依赖放在lombok的依赖上面的话就会出现在对象复制的时候,会将原有数据全变为null的情况,所以一定要将lombok的依赖放在mapperStruct上面

    <dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>${lombok.version}</version>
    </dependency>
    <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 学习路线 / 一对一提问 / 学习打卡 / 每月赠书

    新项目: 仿小红书 (微服务架构)正在更新中... , 全栈前后端分离博客项目 2.0 版本完结啦, 演示链接 http://116.62.199.48/ 。全程手摸手,后端 + 前端全栈开发,从 0 到 1 讲解每个功能点开发步骤,1v1 答疑,直到项目上线。 目前已更新了261小节,累计43w+字,讲解图:1806张,还在持续爆肝中.. 后续还会上新更多项目,目标是将Java领域典型的项目都整一波,如秒杀系统, 在线商城, IM即时通讯,Spring Cloud Alibaba 等等,


    1. 

    2. 

    3. 

    4. 

    最近面试BAT,整理一份面试资料Java面试BATJ通关手册,覆盖了Java核心技术、JVM、Java并发、SSM、微服务、数据库、数据结构等等。

    获取方式:点「在看」,关注公众号并回复 Java 领取,更多内容陆续奉上。

    PS:因公众号平台更改了推送规则,如果不想错过内容,记得读完点一下在看,加个星标,这样每次新文章推送才会第一时间出现在你的订阅列表里。

    「在看」支持小哈呀,谢谢啦