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

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

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

1优点

  • MapStruct编译期生成映射代码,所以可以在编译时暴露映射错误的代码,让错误提前暴露;

  • 因为使用setter/getter方式,而非反射方式,所以可以更快的执行效率;

  • 可以实现深拷贝,自动类型转换,如枚举转换;

  • 进行自定义的映射,多种映射方式,下边具体说明;

2性能对比

图片

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

weixin_37576193

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值