Bootstrap

MapStruct-JavaBean映射工具使用指南

6c4523e166387fd211ea5a1fa275655a.gif

在软件开发中,对象之间的转换是一项常见的任务,尤其是在处理数据模型间的映射时。传统的做法,如使用JavaBeanUtils,可能会导致性能下降,而手动编写转换代码则效率低下且易出错。为了解决这些问题,MapStruct应运而生。MapStruct是一个强大的代码生成器,遵循约定优于配置的原则,使得对象间的映射变得简单、高效且类型安全。它在编译时生成映射代码,确保了高性能,并通过自动化减少开发工作,降低了维护成本。

MapStruct通过注解处理器集成到构建工具和IDE中,简化了集成流程。在实际应用中,我们可以创建一个单独的转换层,集中管理所有映射代码,保持代码的整洁和模块化。此外,MapStruct支持多种映射策略,包括基本类型转换、枚举与字符串之间的映射,甚至复杂的对象和集合转换。通过自定义注解,我们可以处理特殊场景,如空值处理、日期格式化、自定义表达式等。

本文将引导读者逐步了解MapStruct的引入、基本用法,以及在实际开发中的复杂映射场景,帮助开发者充分利用MapStruct提高开发效率和代码质量。

294536c3506a8a55d7fadf225fb84edf.png

介绍

  What

MapStruct是一个代码生成器,它基于约定优于配置的方法,极大地简化了Java bean类型之间的映射实现。


生成的映射代码使用普通的方法调用,因此速度快、类型安全且易于理解。

  Why

89e129055cfcefc57897b5f80cefb5ea.png

多层应用程序通常需要在不同的对象模型(例如实体和DTO)之间进行映射。编写这样的映射代码是一项乏味且容易出错的任务。MapStruct旨在通过尽可能地自动化来简化这项工作。


与其他映射框架相比,MapStruct在编译时生成bean映射,这确保了高性能,允许快速的开发人员反馈和彻底的错误检查。

  How

MapStruct 是一个注解处理器,它插入到 Java 编译器中,可用于命令行构建(Maven、Gradle 等)以及首选的 IDE。

MapStruct 使用合理的默认值,但在配置或实现特殊行为时会采取措施。

解决的痛点:

  1. BeanUtils使用反射的方式进行对象转换赋值,对于需要多次转换场景中,对于性能影响非常严重。尤其是多属性的大对象。

  2. 若不使用BeanUtils赋值转换,对于动辄上百个属性的大对象,需要手动get/set方法赋
    值,编码效率低下。

  3. 对于项目中需要有一个统一管理对象转换的平台,代码高内聚,降低后续维护成本。

实践之路:

  • 引入指南

<properties>
  <maven.compiler.source>8</maven.compiler.source>
  <maven.compiler.target>8</maven.compiler.target>
  <java.version>1.8</java.version>
  <org.mapstruct.version>1.5.3.Final</org.mapstruct.version>
  <!--lombok 版本确保在1.16.16以下版本,高版本存在与mapstruct冲突情况-->
  <org.projectlombok.version>1.16.14</org.projectlombok.version>
</properties>
<dependencies>
  <dependency>
    <groupId>org.mapstruct</groupId>
    <artifactId>mapstruct</artifactId>
    <version>${org.mapstruct.version}</version>
  </dependency>
  <dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>${org.projectlombok.version}</version>
    <scope>provided</scope>
  </dependency>
</dependencies>
<build>
  <plugins>
    <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-compiler-plugin</artifactId>
      <configuration>
        <source>${java.version}</source>
        <target>${java.version}</target>
        <annotationProcessorPaths>
          <path>
            <groupId>org.mapstruct</groupId>
            <artifactId>mapstruct-processor</artifactId>
            <version>${org.mapstruct.version}</version>
          </path>
          <path>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>${org.projectlombok.version}</version>
          </path>
        </annotationProcessorPaths>
      </configuration>
    </plugin>
  </plugins>
</build>
  • 工程结构

工程新增transfer层,对象转换代码统一放在此module中进行管理维护,避免重复建设。

5bee913ce84e7ac79e5850a43ede4b5c.png

Coding Demo
  初始化demo
  • 单个源类转换

首先我们项目中应该会创建OrderInfo类和对应的OrderInfoDTO类

@Getter
@Setter
public class OrderInfo {
    private Long id;
    private Integer dcId;
    private Integer storeId;
}
@Getter
@Setter
public class OrderInfoDTO {
    private Long id;
    private Integer dcId;
    private Integer storeId;
}

定义一个接口,增加org.mapstruct.Mapper.@Mapper注解,componentModel ="spring"代表将MapStruct生成的实现类支持以Spring依赖注入的方式进行管理。

@Mapper(componentModel = "spring")
public interface OrderInfoTransferInterface {
    OrderInfoDTO toDTOByOrderInfo(OrderInfo order);
}

编译后自动生成代码目录:

4f208a98fad11efba2c9f94102cdfc63.png

自动生成代码如下,mapStruct会自动映射相关字段并调用setter/getter赋值并返回,这样一个最基础的通过MapStruct自动生成Java Bean之间转换的demo就完成了。

@Generated(
    value = "org.mapstruct.ap.MappingProcessor",
    date = "2024-05-20T17:54:22+0800",
    comments = "version: 1.5.3.Final, compiler: javac, environment: Java
1.8.0_333 (Oracle Corporation)"
)
@Component
public class OrderInfoTransferDemoInterfaceImpl extends OrderInfoTransferDemoInterface {
    @Override
    public OrderInfoDTO toDTOByOrderInfo(OrderInfo orderInfo) {
        if ( orderInfo == null ) {
            return null;
        }
        OrderInfoDTO orderInfoDTO = new OrderInfoDTO();
        orderInfoDTO.setId( orderInfo.getId() );
        orderInfoDTO.setDcId( orderInfo.getDcId() );
        orderInfoDTO.setStoreId( orderInfo.getStoreId() );
        return orderInfoDTO;
    }
}

MapStruct的默认规则是源类和目标类的对象属性值映射转换时,相同属性名的值会直接映射赋值,如果属性类型不一致,会做基础数据类型的转换并赋值。

  1. 基本类型及其对应的包装类之间。比如int和Integer,float和Float,long和Long,boolean和Boolean等。

  2. 任意基本类型与任意包装类之间。如int和long,byte和Integer等。

  3. 所有基本类型及包装类与String之间。如boolean和String,Integer和String,float和String等。

  4. 枚举和String之间。

  5. Java大数类型 (java.math.BigInteger, java.math.BigDecimal) 和Java基本类型(包括其包装类)与String之间。

实际开发过程中的情况有很多种转换的情况:

  1. 通过在方法上添加 @Mapping 注解方式,指定对应的source和 target 对应的属性,如果目标对象和源对象的属性名一致,则无需指定。参考下面(1)

  2. @Mapping#defaultValue 属性代表如果源对象的该属性值为null 时,赋值对应的默认值。参考下面(2)

  3. 可以通过表达式去进行复杂对象/枚举属性的赋值,例如(3):

    OrderInfo.stateEnum为SoStateEnum类型,OrderInfoDTO.state为Integr类型

  4. 对于某些数据想做脱敏处理,可以全部赋值为常量值,参考@Mapping#constant

  5. 日期类型和String类型的相互转换格式化,可使用@Mapping#dateFormat,参考(5)

  6. 数字类型和String类型的相互转换并格式化,可使用@Mapping#numberFormat,参考(6)

对于源对象和目标对象属性名和类型一致的,无需添加Mapping注解进行映射关系指定。

其他常用特性请参考官方文档:
https://mapstruct.org/documentation/stable/reference/html/#basic-mappings

@Mapper(componentModel = "spring")
public interface OrderInfoTransferInterface {
    /**
     * 单源类 注解方式
     * @param orderInfo
     * @return
     */
    /**(1)/(2)为空时赋值为默认值**/
    @Mapping(source = "creatorErp", target = "creator", defaultValue = "zha
    /**(3)对象属性可以get相应对象赋值**/
    @Mapping(source = "stateEnum.state", target = "state")
     /**(4)全部赋值常量值处理为-1**/
    @Mapping(constant = "-1L", target = "id")
     /**(5)日期格式化 LocalDateTime -> String **/
    @Mapping(source = "createTime", target = "createTime", dateFormat = "yyy
    /**(5)日期格式化 String -> LocalDateTime **/
    @Mapping(source = "submitDate", target = "submitDate", dateFormat = "yy
    /**(6)数字格式化 Double -> String **/
    @Mapping(source = "totalAmt", target = "totalAmt", numberForm
    OrderInfoDTO toDTOByOrderInfo(OrderInfo orderInfo);
}

编译后自动生成代码如下,为了方便对应上,下面代码注释是我手动加上的:

@Generated(
    value = "org.mapstruct.ap.MappingProcessor",
    date = "2024-05-20T10:56:38+0800",
    comments = "version: 1.5.3.Final, compiler: javac, environment: Java 1.8
)
@Component
public class OrderInfoTransferDemoInterface1Impl implements OrderInfoTransferDemoInterface1
    private final DateTimeFormatter dateTimeFormatter_yyyy_MM_dd_HH_mm_ss_11333195168 = DateTimeFormatter.ofPattern( "yyyy-MM-dd HH:mm:ss" );
    @Override
    public OrderInfoDTO toDTO(OrderInfo source) {
        if ( source == null ) {
            return null;
        }
        OrderInfoDTO orderInfoDTO = new OrderInfoDTO();
        /**(1)/(2)为空时赋值为默认值**/
        if ( source.getCreatorErp() != null ) {
            orderInfoDTO.setCreator( source.getCreatorErp() );
        }
        else {
            orderInfoDTO.setCreator( "zhangsan" );
        }
        /**(3)对象属性可以get相应对象赋值**/
        orderInfoDTO.setState( sourceStateState( source ) );
        /**(4)全部赋值常量值处理为-1**/
        orderInfoDTO.setId( (long) -1L );
        /**(5)日期格式化 LocalDateTime -> String **/
        if ( orderInfo.getCreateTime() != null ) {
            orderInfoDTO.setCreateTime( dateTimeFormatter_yyyy_MM_dd_HH_mm_ss_11333195168.format( orderInfo.getCreateTime() ) );
        }
        /**(5)日期格式化 String -> LocalDateTime **/
        if ( orderInfo.getSubmitDate() != null ) {
            orderInfoDTO.setSubmitDate( LocalDateTime.parse( orderInfo.getSubmitDate(), dateTimeFormatter_yyyy_MM_dd_HH_mm_ss_11333195168 ) );
        }
        /**(6)数字格式化 Double -> String **/
        if ( orderInfo.getTotalAmt() != null ) {
            orderInfoDTO.setTotalAmt( new DecimalFormat( "$#.00" ).format( orderInfo.getTotalAmt() ) );
        }
        orderInfoDTO.setDcId( source.getDcId() );
        orderInfoDTO.setStoreId( source.getStoreId() );
        return orderInfoDTO;
    }


    private Integer sourceStateState(OrderInfo order) {
        if ( order == null ) {
            return null;
        }
        SoStateEnum state = order.getState();
        if ( state == null ) {
            return null;
        }
        Integer state1 = state.getState();
        if ( state1 == null ) {
            return null;
        }
        return state1;
    }
}
  • 多个源类转换

场景:多个源对象转换成一个目标对象,只需在source赋值时指定对象名,属性即可。会调用对应的对象的getter方法进行映射赋值。


补充:如果多源对象有相同名称的属性(例如下面源对象order和dcInfo拥有同名属性),则编译报错: Several possible source properties for target property XXX. 需要指定赋值一个源对象的对应属性。

@Mapper(componentModel = "spring")
public interface OrderInfoTransferInterface {
    @Mapping(source = "orderInfo.id", target = "id")
    @Mapping(source = "orderInfo.dcId", target = "dcId")
    @Mapping(source = "dcInfo.dcName", target = "dcName")
    OrderInfoDTO toDTOByOrderInfo(OrderInfo orderInfo,DcInfo dcInfo);
}
  • 子对象映射转换

@Data
@NoArgsConstructor
@AllArgsConstructor
public class OrderInfo {
    private Long id;
    private Integer dcId;
    private Integer storeId;
    private List<OrderInfoDetail> orderDetails;
}


@Data
@NoArgsConstructor
@AllArgsConstructor
public class OrderInfoDetail {
    private Long orderId;
    private Long skuId;
    private Integer applyNum;
}

场景:OrderInfo有子对象属性orderDetails类型为List, 这时该如何转换映射呢?

1)首先定义子对象的映射关系

@Mapper(componentModel = "spring")
public interface OrderInfoDetailTransferDemoInterface {
    OrderInfoDetailDTO toDetailDTO(OrderInfoDetail orderInfoDetail);
}

2)指定父对象映射关系OrderInfoTransferDemoInterface中引用该子对象转换关系OrderInfoDetailTransferDemoInterface(使用@Mapper#uses引用)

@Mapper(componentModel = "spring", uses = {OrderInfoDetailTransferDemoInterfa.class})
public interface OrderInfoTransferDemoInterface {
    OrderInfoDTO toDTO(OrderInfo source);
}

3)引用子对象的映射关系后,由于我们指定映射关系交由Spring统一管理,自动生成的实现类将自动注入该依赖,编译后自动生成父对象的映射关系代码如下:

@Generated(
    value = "org.mapstruct.ap.MappingProcessor",
    date = "2024-03-13T15:31:30+0800",
    comments = "version: 1.5.3.Final, compiler: javac, environment: Java
1.8.0_333 (Oracle Corporation)"
)
@Component
public class OrderInfoTransferDemoInterface1Impl implements OrderInfoTransferDemoInterface {
    @Autowired
    private OrderInfoDetailTransferDemoInterface orderInfoDetailTransferDemoInterface;
    @Override
    public OrderInfoDTO toDTO(OrderInfo source) {
        if ( source == null ) {
            return null;
        }
        OrderInfoDTO orderInfoDTO = new OrderInfoDTO();
        orderInfoDTO.setId( source.getId() );
        orderInfoDTO.setDcId( source.getDcId() );
        orderInfoDTO.setStoreId( source.getStoreId() );
        orderInfoDTO.setOrderDetails( orderInfoDetailListToOrderInfoDetailDTOList( source.getOrderDetails() ) );
        return orderInfoDTO;
    }
    protected List<OrderInfoDetailDTO> orderInfoDetailListToOrderInfoDetailDTOList(List<OrderInfoDetailD> list) {
        if ( list == null ) {
            return null;
        }
        List<OrderInfoDetailDTO> list1 = new ArrayList<OrderInfoDetailDTO>( list.size() );
        for ( OrderInfoDetail orderInfoDetail : list ) {
            list1.add( orderInfoDetailTransferDemoInterface.toDetailDTO(orderInfoDetail ) );
        }
        return list1;
    }
}


@Generated(
    value = "org.mapstruct.ap.MappingProcessor",
    date = "2024-03-13T15:31:30+0800",
    comments = "version: 1.5.3.Final, compiler: javac, environment: Java
1.8.0_333(Oracle Corporation)"
)
@Component
public class OrderInfoDetailTransferDemoInterface1Impl implements OrderInfoDetailTransferDemoInterface1 {
    @Override
    public OrderInfoDetailDTO toDetailDTO(OrderInfoDetail orderInfoDetail) {
        if (orderInfoDetail == null) {
            return null;
        }
        OrderInfoDetailDTO orderInfoDetailDTO = new OrderInfoDetailDTO();
        orderInfoDetailDTO.setOrderId(orderInfoDetail.getOrderId());
        orderInfoDetailDTO.setSkuId(orderInfoDetail.getSkuId());
        orderInfoDetailDTO.setApplyNum(orderInfoDetail.getApplyNum());
        return orderInfoDetailDTO;
    }
}
  复杂场景映射关系转换

上篇主要阐述了通用场景中比较简易的映射关系指定,接下来主要讲解下对于我们实际开发中会存在多种复杂场景的指定映射关系的使用:在一个转换器中存在多种映射关系时,如何解决MapStruct无法匹配的问题。

首先我们需要了解官方文档对于多种映射关系匹配的默认规则:例如对于 A类型 -> B类型的转换关系,默认按照匹配相同映射关系的方法进行匹配,若存在多个相同类型的转换关系时,未指定映射关系时,编译时则会报错:java: Ambiguous mapping methods found for mapping XXXX

  • 集合类转换指定映射关系

场景:OrderInfoDetailTransferDemoInterface中有多种OrderInfoDetail ->OrderInfoDetailDTO映射关系:

  • toDetailDTO(source)

  • convertToDetailDTO(source)

指定映射关系实现:
结合org.mapstruct.@Named#value 和org.mapstruct.@IterableMapping

#qualifiedByName来指定元素转换的映射关系即可参照下面代码实例toDetailDTOList集合的元素转换时会使用toDetailDTO方法。

@Mapper(componentModel = "spring")
public interface OrderInfoDetailTransferDemoInterface {
    @Named("toDetailDTO")
    OrderInfoDetailDTO toDetailDTO(OrderInfoDetail orderInfoDetail);
    @Named("convertToDetailDTO")
    OrderInfoDetailDTO convertToDetailDTO(OrderInfoDetail orderInfoDetail);
    @IterableMapping( qualifiedByName = "toDetailDTO")
    List<OrderInfoDetailDTO> toDetailDTOList(List<OrderInfoDetail> detailList);
}
  • 指定子对象映射关系

场景:OrderInfoDetailTransferDemoInterface中有多种List -> List映射关系

指定映射关系实现:
结合org.mapstruct.@Named#value 和org.mapstruct.@Mapping#qualifiedByName 来指定子对象集合元素转换的映射关系即可,
OrderInfoTransferDemoInterface#toDTO的实现方法会根据匹配规则调用
OrderInfoDetailTransferDemoInterface#toDetailDTOCollection进行转换。

补充:这里qualifiedByName = "toDetailDTOCollection"或者qualifiedByName ="convertToDetailDTO"均可,实现时只是映射关系的实现和调用方法不同。

@Mapper(componentModel = "spring",uses = {OrderInfoDetailTransferDemoInterface.class})
public interface OrderInfoTransferDemoInterface {
    @Mapping(source = "orderDetails", target = "orderDetails", qualifiedByName = "toDetailDTOCollection")
    OrderInfoDTO toDTO(OrderInfo source);
}
@Mapper(componentModel = "spring")
public interface OrderInfoDetailTransferDemoInterface {
    @Named("toDetailDTO")
    OrderInfoDetailDTO toDetailDTO(OrderInfoDetail orderInfoDetail);
    @Named("convertToDetailDTO")
    OrderInfoDetailDTO convertToDetailDTO(OrderInfoDetail orderInfoDetail);
    @IterableMapping( qualifiedByName = "toDetailDTO")
    List<OrderInfoDetailDTO> toDetailDTOList(List<OrderInfoDetailD> detail);
    @Named("toDetailDTOCollection")
    @IterableMapping( qualifiedByName = "convertToDetailDTO")
    List<OrderInfoDetailDTO> toDetailDTOCollection(List<OrderInfoDetailD> detailList);
}
  • 自定义扩展映射关系

在了解了以上特性后,相信已经能够满足通用场景下的对象转换关系的开发需求,下面介绍几种关于MapStruct的进阶操作:

自定义方法扩展信息映射

场景:在已有的映射关系赋值完成后,需要一个入口来自定义一些复杂逻辑的对象扩展信息赋值。

实现方式:使用接口实现自定义方法并结合org.mapstruct.@BeforeMapping、
org.mapstruct.@AfterMapping来进行对象关系对映射前、映射完成后的赋值。

补充:前置方法、后置方法,若返回值不是null,则会作为映射方法的返回值。

@Mapper(componentModel = "spring", uses = {OrderInfoDetailTransferDemoInterface.class})
public interface OrderInfoTransferDemoInterface1 {
    /**
     * beforeToDTO
     */
    @BeforeMapping
    default void beforeToDTO(@MappingTarget OrderInfoDTO dto) {
        if (CollectionUtils.isEmpty(dto.getOrderDetails())) {
            dto.setOrderDetails(Lists.newArrayList());
        }
    }


    @Mapping(source = "source.orderDetails", target = "orderDetails", qualif
    OrderInfoDTO toDTO(OrderInfo source, Map<Integer, StoreInfoDTO>storeInfoMap);


    /**
     * afterToDTO
     */
    @AfterMapping
    default OrderInfoDTO afterToDTO(OrderInfo source, Map<Integer, StoreInfo> storeInfoMap) {
        StoreInfoDTO storeInfoDTO = storeInfoMap.get(source.getStoreId());
        dto.setStoreName(Objects.nonNull(storeInfoDTO) ? storeInfoDTO.getStoreName;
        return dto;
    }
}
//以下为mapstruct自动生成代码
@Generated(
    value = "org.mapstruct.ap.MappingProcessor",
    date = "2023-03-13T18:31:16+0800",
    comments =
    "编译后自动生成的代码,会在映射关系赋值前调用自定义方法beforeToDTO
)
@Component
public class OrderInfoTransferDemoInterface1Impl implements OrderInfoTransferDemoInterface1{


    @Autowired
    private OrderInfoDetailTransferDemoInterface orderInfoDetailTransferDemoInterface;


    @Override
    public OrderInfoDTO toDTO(OrderInfo source, Map<Integer, StoreInfoDTO> storeInfoMap) {
        if (source == null && storeInfoMap == null) {
            return null;
        }
        OrderInfoDTO orderInfoDTO = new OrderInfoDTO();
        beforeToDTO(orderInfoDTO);
        if (source != null) {
            orderInfoDTO.setOrderDetails(orderInfoDetailTransferDemoInterface.toDetailDTOList(source.getOrderDetailList()));
            orderInfoDTO.setId(source.getId());
            orderInfoDTO.setDcId(source.getDcId());
            orderInfoDTO.setStoreId(source.getStoreId());
        }
        OrderInfoDTO target = afterToDTO(source, storeInfoMap, orderInfoDTO);
        if (target != null) {
            return target;
        }
        return orderInfoDTO;
    }
}

指定自定义扩展方法

场景:存在多个前置方法或者后置方法的情况下,如何指定映射方法选择执行前置方法/后置方法呢?

实现方式:
结合org.mapstruct.@Named#value 和org.mapstruct.@BeanMapping#qualifiedByName 指定执行的前置方法/后置方法。

@Mapper(componentModel = "spring", uses = {OrderInforDetailTransferDemoInterface.class})
public interface OrderInforTransferDemoInterface1 {
    /**
     * beforeToDTO
     */
    @BeforeMapping
    @Named("beforeAndAfterToDTO")
    default void beforeToDTO(@MappingTarget OrderInforDTO dto) {
        //自定义实现
    }


    /**
     * beforeAndAfterToDTOMethod
     */
    @BeforeMapping
    @Named("beforeAndAfterToDTOMethod")
    default void beforeToDTOMethod(@MappingTarget OrderInforDTO dto) {
        //自定义实现
    }


    /**
     * afterToDTO
     */
    @AfterMapping
    @Named("beforeAndAfterToDTO")
    default OrderInforDTO afterToDTO(OrderInfor source, Map<Integer, StoreInfo> storeInfoMap) {
        //自定义实现
        return null;
    }


    /**
     * afterToDTOMethod
     */
    @AfterMapping
    @Named("beforeAndAfterToDTOMethod")
    default void afterToDTOMethod(OrderInfor source, Map<Integer, StoreInfo> storeInfoMap) {
        //自定义实现
    }


    @BeanMapping(qualifiedByName = "beforeAndAfterToDTO")
    OrderInforDTO toDTO(OrderInfor source, Map<Integer, StoreInfo> storeInfoMap);
}

其他实现方式

抽象类的方式实现自定义扩展方法同样可行。

@Mapper(componentModel = "spring", uses = {OrderInforDetailTransferDemoInterface.class})
public abstract class OrderInforTransferDemoInterface {
    /**
         * 自定义方法
         */
    protected OrderInforDTO toDTO(OrderInfo source) {
        OrderInfoDTO dto = new OrderInfoDTO();
        //.....
        return dto;
    }


    @BeanMapping(qualifiedByName = "after_toDTOByOrderInfo")
    @Mapping(constant = "-1L", target = "id")
    @Mapping(source = "orderDetails", target = "orderDetails", qualifiedByName = "toDetailDTOCollection")
    public abstract OrderInfoDTO toDTOByOrderInfo(OrderInfo orderInfo);
    @AfterMapping
    @Named("after_toDTOByOrderInfo")
    protected void setDetailDtoByJsf(OrderInfo orderInfo, @MappingTarget OrderInfoDTO orderInfoDTO) {
        //自定义后置方法实现
    }
}
  • 自定义表达式

除了自定义方法扩展外,MapStruct同样支持自定义表达式扩展赋值。主要涉及:

  • org.mapstruct.@Mapping#expression

  • org.mapstruct.@Mapping#defaultExpression

自定义表达式实现

表达式在使用时,只是规定了java语言环境,但是里面实际只是字符串,我们需要使用的类需要在@Mapper#imports[] 里引用:


@Mapping#defaultExpression是指源对象的属性值为空时,则赋为自定义表达式;
@Mapping#expression是直接将目标对象的属性值赋值为自定义表达式;

示例代码如下:

@Mapper(componentModel = "spring", imports = {LocalDateTime.class, UUID.class})
public interface OrderInfoTransferDemoInterface1 {
    @Mapping(target = "uuid", expression = "java(UUID.randomUUID().toString()")
    @Mapping(target = "createTime", source = "createTime", defaultExpression = "LocalDateTime.now()")
    OrderInfoDTO toDTO(OrderInfo source);
}


@Generated(
    value = "org.mapstruct.ap.MappingProcessor",
    date = "2024-03-14T11:38:29+0800",
    comments = "version: 1.5.3.Final, compiler: javac, environment: Java 1.8
)
@Component
public class OrderInfoTransferDemoInterface1Impl implements OrderInfoTransferDemoInterface1 {
    @Override
    public OrderInfoDTO toDTO(OrderInfo source) {
        if (source == null) {
            return null;
        }
        OrderInfoDTO orderInfoDTO = new OrderInfoDTO();
        if (source.getCreateTime() != null) {
            orderInfoDTO.setCreateTime(source.getCreateTime());
        } else {
            orderInfoDTO.setCreateTime(LocalDateTime.now());
        }
        orderInfoDTO.setUuid(UUID.randomUUID().toString());
        return orderInfoDTO;
    }
}
其他补充说明
  1. 如果你的类中包含Builder,MapStruct会尝试使用它来创建实例;如果没有的话,MapStruct将通过new关键字进行实例化。这里推荐大家使用new关键字的方式进行实例化。

  2. lombok的@Builder和mapstruct的@AfterMapping有冲突,如果实体引用了@Builder,会导致@AfterMapping方法不生效。需要在映射方法上注明@BeanMapping(builder = @Builder(disableBuilder = true))不使用builder初始化。 

  3. mapstruct的版本与lombok某些版本存在冲突的情况,这里推荐大家使用lombok-1.16.16以下版本搭配mapstruct-1.5.3.Final使用。

  4. 这里只讲述了一些MapStruct的基础使用,其他特性请参照官方网站:
    https://mapstruct.org/

997a09ab8ce5dfe0bd1076475251dbb4.png

团队介绍

我们是淘天集团商家经营工具技术团队,专注于淘宝天猫商家经营工具的研发,涉及订单处理、物流履约、客服业务、财务税务工具以及商品货品供给等多个业务领域。我们致力于构建高效、合规、稳定以及智能化的商家经营工具,帮助商家提升竞争力和运营效率,进而促进淘天集团整体业务的发展。

¤ 拓展阅读 ¤

3DXR技术 | 终端技术 | 音视频技术

服务端技术 | 技术质量 | 数据算法

;