MapStruct-JavaBean映射工具使用指南

news2024/11/24 14:04:27

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技术 | 终端技术 | 音视频技术

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1869043.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

GPU_Gems-物理模型的水模拟

创建一个多网格的平面 void GraphicsWindowBase::RenderPlane() {constexpr int width 150;constexpr int depth 150;constexpr int vertNum width * depth;float length 60.f;if (quadVAO 0){float planeVert[vertNum * 5];float offsetX length / (width - 1.f);float…

低空经济再获新动能!沃飞长空完成新一轮数亿元融资

当下&#xff0c;作为中国"新质生产力"代表的低空经济正在成为新的发展“风口”&#xff0c;全国各地开足马力加速入场。 低空经济有多“火”&#xff1f;政策方面&#xff0c;据不完全统计&#xff0c;已有26个省份的政府工作报告对发展低空经济作出部署&#xff1…

Mysql需要知道的点

目录 一、数据库的三范式是什么 二、Mysql数据库引擎有哪些 三、说说Innodb与MYISAM的区别 四、数据库的事务 五、索引是什么 六、优化手段有哪些 七、简单说一说 drop&#xff0c;delete与truncate的区别 八、什么是视图 九、什么是内连接、左外连接、右外连接&#x…

mysql中in参数过多优化

优化方式概述 未优化前 SELECT * FROM rb_product rb where sku in(1022044,1009786)方案2示例 public static void main(String[] args) {//往list里面设置3000个值List<String> list new ArrayList<>();for (int i 0; i < 3000; i) {list.add(""…

知识图谱介绍及其应用领域分析

1.知识图谱 知识图谱(Knowledge Graph)乃一种精心设计的技术,旨在储存并整合交织的描述性知识信息。此技术通过构建由实体及其相互关系所组成的网络结构,实现对知识的有序组织与呈现。这些实体涵盖广泛的范畴,包括但不限于具体的物体、事件或抽象概念,它们经由多样化的关…

​​植物大战僵尸杂交版直装版v2.1 安卓版:全新策略塔防体验

《植物大战僵尸杂交版直装版》v2.1是由B站UP主“潜艇伟伟迷”精心制作的同人游戏&#xff0c;为策略塔防手游带来了全新的活力。游戏中引入了众多创新的杂交植物&#xff0c;例如结合了向日葵的阳光生成能力和豌豆射手的攻击特性的向日葵豌豆射手&#xff0c;以及拥有寒冰豌豆射…

2024平价蓝牙耳机推荐哪款?百元左右平价蓝牙耳机推荐

在2024的无线耳机市场中&#xff0c;蓝牙耳机已经成为了主流。无论是对于音乐爱好者还是普通消费者&#xff0c;选择一款音质出色、舒适度高且功能齐全的蓝牙耳机还是很重要的。一款好的蓝牙耳机不仅戴在耳朵上很舒服&#xff0c;而且音质还没有任何的杂音&#xff0c;但现在的…

uniapp字体ttf在小程序报错,解决方法

文章目录 导文解决方法1&#xff1a;把字体改成base64格式解决方法2&#xff1a;改成线上模式 导文 报错1&#xff1a; uniapp 小程序报错&#xff1a;app.js错误: Error: Module build failed (from ./node_modules/mini-css-extract-plugin/dist/loader.js): ModuleBuildErro…

【PWN · TcachebinAttack | UAF】[2024CISCN · 华中赛区] note

一道简单的tcache劫持 一、题目 二、思路 存在UAF&#xff0c;libc版本2.31&#xff0c;经典菜单题 1.通过unsorted-bin-attack来leak-libc 2.通过uaf打tcache-bin-attack劫持__free_hook实现getshell 三、EXP from pwn import * context(archamd64,log_leveldebug)ioproce…

Hive-存储-文件格式

一、前言 数据存储是Hive的基础&#xff0c;选择合适的底层数据存储格式&#xff0c;可以在不改变Hql的前提下得到大的性能提升。类似mysql选择适合场景的存储引擎。 Hive支持的存储格式有 文本格式&#xff08;TextFile&#xff09; 二进制序列化文件 &#xff08;SequenceF…

大家都在聊IPD(集成产品开发)?国内IPD体系做的比较好的是哪款项目管理软件?看IBM、华为的研发管理之道!

IPD&#xff08;集成产品开发&#xff09;到底是什么&#xff1f;一套体系&#xff1f;一些流程&#xff1f;还是一种模式&#xff1f; 一、回顾一下&#xff0c;什么是IPD&#xff1f; IPD到底是什么&#xff1f;一套体系&#xff1f;一些流程&#xff1f;还是一种模式&#…

Batch学习及应用案例

一、介绍 Batch是一种Windows操作系统中使用的批处理脚本语言&#xff0c;用于自动化执行一系列命令和操作。通过编写批处理脚本&#xff0c;可以实现自动化完成重复性或繁琐的任务&#xff0c;提高工作效率。 Batch脚本可以使用内置的命令和命令行工具&#xff0c;以及调用其…

学校机器该maven环境

在学校机器上 安装maven配置idea中的maven 后&#xff0c;发现无法运行&#xff0c; 推测是学校电脑上idea版本和我们下的maven 可能不太匹配。 学校的电脑上idea有集成的maven&#xff0c;但默认配置是访问国外的服务器 解决办法&#xff1a; 下载分享给各位同学的压缩包m…

顺序表--数据结构第一关

顺序表 数据结构概念 定义&#xff1a;数据结构是计算机存储、组织数据的⽅式 根据学过C语言的基础上&#xff0c;数组是最简单的数据结构 顺序表的底层就是数组 为什么呢&#xff1f; 例子如下&#xff1a; int arr[100]{1,2,3,4,5}; //修改某一个数据&#xff1a;arr[…

vcruntime140_1.dll是什么东东?vcruntime140_1.dll缺失的8个解决方法

当电脑出现找不到vcruntime140_1.dll,或vcruntime140_1.dll丢失无法打开软件怎么办&#xff1f;小编今天在本文详细为大家介绍解决方法与介绍vcruntime140_1.dll究竟是什么等vcruntime140_1.dll的问题。 一、vcruntime140_1.dll文件是什么 文件概述定义与功能 vcruntime140_…

校园圈子小程序系统搭建需求和需要哪些功能?APP小程序H5前后端源码交付

功能&#xff1a;小程序授权登陆&#xff0c;支持app双端&#xff0c;小程序&#xff0c;h5&#xff0c;pc端&#xff0c;手机号登陆&#xff0c;发帖&#xff0c;建圈子、发活动。可置顶推荐帖子&#xff0c;关注、粉 丝、点赞等。可作为圈子贴吧、小红书、校园社区、表白墙、…

【02】从0到1构建AI生成思维导图应用 -- 编写主页

【02】从0到1构建AI生成思维导图应用 – 编写主页 大家好&#xff01;最近自己做了一个完全免费的AI生成思维导图的网站&#xff0c;支持下载&#xff0c;编辑和对接微信公众号&#xff0c;可以在这里体验&#xff1a;https://lt2mind.zeabur.app/ 上一章&#xff1a;https:/…

【AI大模型RAG】深入探索检索增强生成(RAG)技术

目录 1. 引言2. RAG技术概述2.1 RAG技术的定义2.2 RAG技术的工作原理2.3 RAG技术的优势2.4 RAG技术的应用场景 3. RAG的工作流程3.1 输入处理3.2 索引建立3.3 信息检索3.4 文档生成3.5 融合与优化 4. RAG范式的演变4.1 初级 RAG 模型4.2 高级 RAG 模型4.3 模块化 RAG 模型优化技…

生命在于学习——Python人工智能原理(2.5.1)

五、Python的类与继承 5.1 Python面向对象编程 在现实世界中存在各种不同形态的事物&#xff0c;这些事物之间存在各种各样的联系。在程序中使用对象来映射现实中的事物&#xff0c;使用对象之间的关系描述事物之间的联系&#xff0c;这种思想用在编程中就是面向对象编程。 …

nodejs国内源下载

nodejs的官网下载太慢了 可以尝试网盘下载快一点 夸克网盘分享夸克网盘是夸克推出的一款云服务产品&#xff0c;功能包括云存储、高清看剧、文件在线解压、PDF一键转换等。通过夸克网盘可随时随地管理和使用照片、文档、手机资料&#xff0c;目前支持Android、iOS、PC、iPad。…