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

6de109c86dfa755b6d14d3f0a156774b.jpeg来源:juejin.cn/post/7333458486435987494

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

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

5e139bc6ceb4646a682ddff579b4b499.gif

  • 为什么使用MapperStruct

  • 怎么使用MapperStruct

  • 总结


在实际的业务开发中,我们经常会碰到BO、PO、DTO等对象属性之间的赋值,当属性较多的时候我们使用get,set的方式进行赋值的工作量相对较大,因此很多人会选择使用spring提供的拷贝工具BeanUtilscopyProperties方法完成对象之间属性的拷贝。

通过这种方式可以很大程度上降低我们手动编写对象属性赋值代码的工作量,既然它那么方便为什么还不建议使用呢?

我总结为以下几点:

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

在实际开发中,很可能会出现同一字段在不同的类中定义的类型不一致,例如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快很多很多

放张参考图给大家参考

a9aca24d48ddee5961905bc5631d11f3.png

图片

那么为什么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 等等,戳我加入学习,已有1500+小伙伴加入(早鸟价超低)

b58fdac2826c38668af36bb3f3c1d98b.gif

e24951239c5b79a34bbf145acc35b139.jpeg

 
 

314d685921547630b603ba0741f844fd.gif

 
 
 
 
1. 我的私密学习小圈子~
2. 升级 JDK17 一个不可拒绝的理由!
3. 利用 Java 反射机制提高 SpringBoot 的代码质量
4. 公司新来小伙,把 MyBatis 替换成 MyBatis-Plus,上线后被开了.....
 
 
最近面试BAT,整理一份面试资料《Java面试BATJ通关手册》,覆盖了Java核心技术、JVM、Java并发、SSM、微服务、数据库、数据结构等等。
获取方式:点“在看”,关注公众号并回复 Java 领取,更多内容陆续奉上。
PS:因公众号平台更改了推送规则,如果不想错过内容,记得读完点一下“在看”,加个“星标”,这样每次新文章推送才会第一时间出现在你的订阅列表里。
点“在看”支持小哈呀,谢谢啦
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
有两种替代BeanUtils.copyProperties的方法可以考虑使用: 1. 使用BeanMapperBeanMapper是一个利用javassist类库实现的替代方案,它可以在大量调用时提供更好的性能。你可以通过以下步骤来使用BeanMapper: - 首先,将BeanMapper添加到你的项目中。 - 然后,创建一个映射器接口,定义源对象和目标对象之间的映射规则。 - 最后,使用映射器接口的实现类来执行对象之间的属性复制操作。 以下是使用BeanMapper替代BeanUtils.copyProperties的示例代码[^1]: ```java // 添加BeanMapper到项目中 <dependency> <groupId>net.sf.cglib</groupId> <artifactId>cglib</artifactId> <version>3.2.5</version> </dependency> // 创建映射器接口 public interface MyMapper { MyMapper INSTANCE = Mappers.getMapper(MyMapper.class); void map(SourceObject source, TargetObject target); } // 执行属性复制操作 SourceObject source = new SourceObject(); TargetObject target = new TargetObject(); MyMapper.INSTANCE.map(source, target); ``` 2. 使用MapStruct:MapStruct是另一个强大的替代方案,它可以比BeanUtils.copyProperties更快地执行实体类之间的转换操作。你可以按照以下步骤来使用MapStruct: - 首先,将MapStruct添加到你的项目中。 - 然后,创建一个映射器接口,定义源对象和目标对象之间的映射规则。 - 最后,使用映射器接口的实现类来执行对象之间的属性复制操作。 以下是使用MapStruct替代BeanUtils.copyProperties的示例代码[^2]: ```java // 添加MapStruct到项目中 <dependency> <groupId>org.mapstruct</groupId> <artifactId>mapstruct</artifactId> <version>1.4.2.Final</version> </dependency> // 创建映射器接口 @Mapper public interface MyMapper { MyMapper INSTANCE = Mappers.getMapper(MyMapper.class); TargetObject map(SourceObject source); } // 执行属性复制操作 SourceObject source = new SourceObject(); TargetObject target = MyMapper.INSTANCE.map(source); ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值