顶级工具,性能爆棚的 Java 实体转换 / 复制神器

小哈学Java

共 13916字,需浏览 28分钟

 ·

2023-11-09 00:22

来源:blog.csdn.net/qq_30231473/article/details/129782881

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


目前, 正在星球内部带小伙伴做第一个项目:全栈前后端分离博客,手摸手,后端 + 前端全栈开发,从 0 到 1 讲解每个功能点开发步骤,1v1 答疑,直到项目上线。目前已更新了125小节,累计18w+字,讲解图:805张,还在持续爆肝中.. 后续还会上新更多项目,目标是将Java领域典型的项目都整一波,如秒杀系统, 在线商城, IM即时通讯,Spring Cloud Alibaba 等等,戳我加入学习,已有400+小伙伴加入(早鸟价超低)

  • 1 优点
  • 2 性能对比
  • 3 使用
    • 依赖
    • 定义转换接口
    • 编译结果
    • 调用
  • 4 插件
    • 特性
  • 5 其他用法
    • 基础映射
    • 映射器添加自定义方法
    • 多个源参数映射
    • 嵌套属性映射到当前目标
    • 表达式方式
    • 更新现有实例
    • Map映射
    • 更多用法
  • 6 总结

Java项目中实体转换无处不在,当实体字段较多或者大批量的进行复制时,通过手工setter/getter显得太LOW,同时兼备高性能要求情况下,MapStruct完全完全能够胜任。

官方解释,MapStruct是一个代码生成器,它基于约定优于配置的方法,极大地简化了Java bean类型之间映射的实现。生成的映射代码使用普通方法调用,因此快速、类型安全且易于理解。因为MapStruct是在编译期间生成setter/getter方法,实际运行时就是直接调用setter/getter,效率会非常高。

1 优点

  • MapStruct编译期生成映射代码,所以可以在编译时暴露映射错误的代码,让错误提前暴露;
  • 因为使用setter/getter方式,而非反射方式,所以可以更快的执行效率;
  • 可以实现深拷贝,自动类型转换,如枚举转换;
  • 进行自定义的映射,多种映射方式,下边具体说明;

2 性能对比

对比对象 10个对象复制1次 1万个对象复制1次 100万个对象复制1次 100万个对象复制5次
MapStruct 0ms 3ms 96ms 281ms
Hutools的BeanUtil 23ms 102ms 1734ms 8316ms
Spring的BeanUtils 2ms 47ms 726ms 3676ms
Apache的BeanUtils 20ms 156ms 10658ms 52355ms
Apache的PropertyUtils 5ms 68ms 6767ms 30694ms

3 使用

依赖

<!-- MapStruct核心,包含了一些必要的注解-->
<dependencies>
    <dependency>
        <groupId>org.mapstruct</groupId>
        <artifactId>mapstruct</artifactId>
        <version>${org.mapstruct.version}</version>
    </dependency>
</dependencies>
<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.8.1</version>
            <configuration>
                <source>1.8</source>
                <target>1.8</target>
                <annotationProcessorPaths>
                   <!-- MapStruct编译,注解处理器,根据注解自动生成Mapper的实现 -->
                    <path>
                        <groupId>org.mapstruct</groupId>
                        <artifactId>mapstruct-processor</artifactId>
                        <version>${org.mapstruct.version}</version>
                    </path>
                </annotationProcessorPaths>
            </configuration>
        </plugin>
    </plugins>
</build>

定义转换接口

/**
 * 测试接口
 *
 * @author reboot
 */
@Mapper
public interface OrderConvertor {
 
    /**
     * 实例
     */
    OrderConvertor INSTANCE = Mappers.getMapper(OrderConvertor.class);
 
    /**
     * OrderDo -> OrderModel
     *
     * @param orderDo 订单实体
     * @return {@link OrderModel}
     */
    OrderModel toModel(OrderDo orderDo);
 
    /**
     * OrderDo -> OrderModel
     *
     * @param orderDos 订单实体
     * @return {@link OrderModel}
     */
    List<OrderModel> toModel(List<OrderDo> orderDos);
 
    /**
     * OrderModel -> OrderDo
     *
     * @param orderModel 订单模型
     * @return {@link OrderDo}
     */
    OrderDo toDo(OrderModel orderModel);
 
    /**
     * OrderModel -> OrderDo
     *
     * @param orderModels 订单模型
     * @return {@link OrderDo}
     */
    List<OrderDo> toDo(List<OrderModel> orderModels);
}

编译结果

图片

MapStruct会自动生成对应接口的实现,并自动完成属性映射关系,List会自动进行批量处理。

调用

/**
 * 订单服务
 *
 * @author reboot
 */
@Service
public class OrderService {
 
    /**
     * 获取订单列表
     *
     * @return {@link List}<{@link OrderModel}>
     */
    public List<OrderModel> getOrderList() {
        // 获取数据库数据DO
        List<OrderDo> result = selectOrderList();
        // 参数转换
        return OrderConvertor.INSTANCE.toModel(result);
    }
}

4 插件

图片

上边的使用方式虽然能够正常使用,但是在一些属性配置映射上和提示上,如果使用插件能够提升使用体验,IDEA中可以直接安装Mapstruct Support插件,当然Eclipse也有对应的插件。

特性

  • 突出显示目标属性和源属性。将目标属性和源属性转到声明的setter / getter中;

  • 错误和快速修复:


    • 缺少@Mapper或@MapperConfig注解检查;
    • 快速修复未映射的目标属性,添加未映射目标属性和忽略未映射目标属性;

图片

5 其他用法

更加详细的内容可以查看官方文档,发布文章时最新版本是 MapStruct 1.5.3.Final.html。

基础映射

@Mapper
public interface CarMapper {
 
    @Mapping(target = "manufacturer"source = "make")
    @Mapping(target = "seatCount"source = "numberOfSeats")
    CarDto carToCarDto(Car car);
 
    @Mapping(target = "fullName"source = "name")
    PersonDto personToPersonDto(Person person);
}

target表示目标属性名,source表示源属性名,一般在目标属性和源属性不同时使用,相同的属性名会自动进行映射。

映射器添加自定义方法

@Mapper
public interface CarMapper {
 
    @Mapping(...)
    ...
    CarDto carToCarDto(Car car);
 
    default PersonDto personToPersonDto(Person person) {
        //hand-written mapping logic
    }
}

自定义方法personToPersonDto并实现,在生成的实现类中会进行覆盖使用。

多个源参数映射

@Mapper
public interface AddressMapper {
 
    @Mapping(target = "description"source = "person.description")
    @Mapping(target = "houseNumber"source = "address.houseNo")
    DeliveryAddressDto personAndAddressToDeliveryAddressDto(Person person, Address address);
 
    @Mapping(target = "description"source = "person.description")
    @Mapping(target = "houseNumber"source = "hn")
    DeliveryAddressDto personAndAddressToDeliveryAddressDto(Person person, Integer hn);
}

存在多个源参数,使用参数名.属性名的方式进行表示,也可以直接使用基础类型的属性名称。

嵌套属性映射到当前目标

@Mapper
 public interface CustomerMapper {
 
     @Mapping( target = "name"source = "record.name" )
     @Mapping( target = "."source = "record" )
     @Mapping( target = "."source = "account" )
     Customer customerDtoToCustomer(CustomerDto customerDto);
 }

当源参数中存在对象属性,可以手动进行映射,或者直接使用"."的方式将对象中的属性全部映射到当前目标对象。

表达式方式

@Mapper
public interface SourceTargetMapper {
 
    SourceTargetMapper INSTANCE = Mappers.getMapper( SourceTargetMapper.class );
 
    @Mapping(
        target = "timeAndFormat",
        expression = "java( new org.sample.TimeAndFormat( s.getTime(), s.getFormat() ) )"
    )
    Target sourceToTarget(Source s);
}

支持使用java代码块进行转换,一般可以将静态方法处理的字段放到这里。

更新现有实例

@Mapper
public interface CarMapper {
 
    void updateCarFromDto(CarDto carDto, @MappingTarget Car car);
}

@MappingTarget源参数,编译时会将carDto参数中的属性映射到car参数中。

Map映射

@Mapper
public interface CustomerMapper {
 
    @Mapping(target = "name"source = "customerName")
    Customer toCustomer(Map<String, String> map);
}

直接将map中的key进行映射。

更多用法

还有更多其他用法,比如:

  • 支持映射定义的public属性;
  • 支持映射参数Builder模式;
  • 使用注入方式引入转换器;
  • 数据类型字段转换,如枚举、日期,支持日期格式化,支持数字类型格式化
  • 集合类型自动转换;
  • 转换Stream;

6 总结

MapStruct还有很多其他高阶特性,限于篇幅文章仅仅列举部分示例,有兴趣的同学可以查看对应文档试试。使用适当的工具有效提高编程效率,在使用工具过程中我们也了解其实现原理,不断提高自身。

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


目前, 正在星球内部带小伙伴做第一个项目:全栈前后端分离博客,手摸手,后端 + 前端全栈开发,从 0 到 1 讲解每个功能点开发步骤,1v1 答疑,直到项目上线。目前已更新了125小节,累计18w+字,讲解图:805张,还在持续爆肝中.. 后续还会上新更多项目,目标是将Java领域典型的项目都整一波,如秒杀系统, 在线商城, IM即时通讯,Spring Cloud Alibaba 等等,戳我加入学习,已有400+小伙伴加入(早鸟价超低)

   
   

        
        
            
            
                   
                   
   
   
           
           
              
              

1. 我的私密学习小圈子~

2. 卧槽!红警源代码居然开源了....

3. 注解方式优雅的实现 Redisson 分布式锁

4. MySQL到底是 join 性能好,还是in一下更快呢?

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

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

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

“在看”支持小哈呀,谢谢啦

浏览 1347
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报