开发者

性能爆棚的实体转换复制工具MapStruct使用详解

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

    引言

    Java项目中实编程体转换无处不在,当实体字段较多或者大批量的进行复制时,通过手工setter/getter显得太LOW,同时兼备高性能要求情况下,MapStruct完全完全能够胜任。官方解释,MapStruct是一个代码生成器,它基于约定优于配置的方法,极大地简化了Java bean类型之间映射的实现。生成的映射代码使用普通方法调用,因此快速、类型安全且易于理解。因为MapStruct是在编译期间生成setter/getter方法,实际运行时就是直接调用setter/getter,效率会非常高。

    优点

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

    性能对比

    对比对象10个对象复制1次1万个对象复制1次100万个对象复制1次100万个对象复制5次
    MapStruct0ms3ms96ms281ms
    Hutools的BeanUtil23ms102ms1734ms8316ms
    Spring的BeanUtils2ms47ms726ms3676ms
    Apache的BeanUtils20ms156ms10658ms523javascript55ms
    Apache的PropertyUtils5ms68ms6767ms30694ms

    使用

    依赖

    <!-- 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>
                     php       <artifactId>mapstruct-processor</artifactId>
                js            <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使用详解

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

    调用

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

    插件

    性能爆棚的实体转换复制工具MapStruct使用详解

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

    特性

    • 突出显示目标属性和源属性。将目标属性和源属性转到声明的setter / getter中;
    • 错误和快速修复:
      • 缺少@Mapper或@MapperConfig注解检查;
      • 快速修复未映射的目标属性,添加未映射目标属性和忽略未映射目标属性;

    性能爆棚的实体转换复制工具MapStruct使用详解

    其他用法

    更加详细的内容可以查看官方文档,发布文章时最新版本是 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);
        dpythonefault 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.开发者_JS培训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模式;
    • 使用注入方式引入转换器;
    • 数据类型字段转换,如枚举、日期,支持日期格式化,支持数字类型格式化,具体可以看 Implicit type conversions;
    • 集合类型自动转换;
    • 转换Stream;
    • ......

    总结

    MapStruct还有很多其他高阶特性,限于篇幅文章仅仅列举部分示例,有兴趣的同学可以查看对应文档试试。使用适当的工具有效提高编程效率,在使用工具过程中我们也了解其实现原理,不断提高自身。后边有时间也把MapStruct实现原理拿出来讲讲,跟大家一起学习进步!

    以上就是性能爆棚的实体转换复制工具MapStruct使用详解的详细内容,更多关于MapStruct实体转换复制的资料请关注我们其它相关文章!

    0

    上一篇:

    下一篇:

    精彩评论

    暂无评论...
    验证码 换一张
    取 消

    最新开发

    开发排行榜