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 {@Overridepublic 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", numberFormOrderInfoDTO 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 OrderInfoTransferDemoInterface1private final DateTimeFormatter dateTimeFormatter_yyyy_MM_dd_HH_mm_ss_11333195168 = DateTimeFormatter.ofPattern( "yyyy-MM-dd HH:mm:ss" );@Overridepublic 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 {@Autowiredprivate OrderInfoDetailTransferDemoInterface orderInfoDetailTransferDemoInterface;@Overridepublic 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 {@Overridepublic 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*/@BeforeMappingdefault void beforeToDTO(@MappingTarget OrderInfoDTO dto) {if (CollectionUtils.isEmpty(dto.getOrderDetails())) {dto.setOrderDetails(Lists.newArrayList());}}@Mapping(source = "source.orderDetails", target = "orderDetails", qualifOrderInfoDTO toDTO(OrderInfo source, Map<Integer, StoreInfoDTO>storeInfoMap);/*** afterToDTO*/@AfterMappingdefault 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{@Autowiredprivate OrderInfoDetailTransferDemoInterface orderInfoDetailTransferDemoInterface;@Overridepublic 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 {@Overridepublic 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.mzph.cn/news/861604.shtml

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

相关文章

为何整个 AI 领域都朝着 AI Agents 这一方向发展?

编者按&#xff1a; 当前大热的大语言模型和检索增强生成模型&#xff0c;虽然在语言理解和内容生成方面取得了突破性的进展&#xff0c;但仍然存在诸多限制。它们缺乏根据目标导引行为、持续学习和与环境交互的能力&#xff0c;难以应对复杂多变的现实场景需求。 今天为大家带…

Go Error 处理

&#x1f49d;&#x1f49d;&#x1f49d;欢迎莅临我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:「stormsha的主页」…

HarmonyOS(39) Preferences 入门指南

Preferences Preferences应用实例初始化preferences保存并持久化数据获取数据 参考资料 Preferences Android开发程序员对此应该很熟悉&#xff0c;HarmonyOS里的Preferences跟Android里的SharePreference差不多&#xff0c;应用提供Key-Value键值型的数据处理能力&#xff0c…

HTTP性能测试工具 —— wrk!

wrk性能测试工具详解 wrk是一款轻量级但功能强大的HTTP基准测试工具&#xff0c;主要用于在单机多核CPU环境下对HTTP服务进行性能测试。它通过利用系统自带的高性能I/O机制&#xff08;如epoll、kqueue等&#xff09;&#xff0c;结合多线程和事件模式&#xff0c;能够产生大量…

AI 助力的在线 Excel 表格:真正的革命还是市场噱头?

在当今数字化和自动化的时代&#xff0c;人工智能&#xff08;AI&#xff09;技术被广泛应用于各种领域&#xff0c;从智能手机到工业生产&#xff0c;无所不在。最近&#xff0c;一些产品声称通过AI技术来增强传统的办公软件&#xff0c;如在线Excel表格。例如&#xff0c;Cha…

1966 ssm 流浪猫领养网站系统开发mysql数据库web结构java编程计算机网页源码eclipse项目

一、源码特点 ssm 流浪猫领养网站系统是一套完善的信息系统&#xff0c;结合springMVC框架完成本系统&#xff0c;对理解JSP java编程开发语言有帮助系统采用SSM框架&#xff08;MVC模式开发&#xff09;&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/…

华为od-C卷200分题目3 - 两个字符串间的最短路径问题

华为od-C卷200分题目3 - 两个字符串间的最短路径问题 题目描述 给定两个字符串&#xff0c;分别为字符串A与字符串B。 例如A字符串为ABCABBA&#xff0c;B字符串为CBABAC可以得到下图m*n的二维数组&#xff0c;定义原点为(0, 0)&#xff0c;终点为(m, n)&#xff0c;水平与垂…

五月软考满分范文“模型驱动架构设计方法及其应用”,软考高级,系统架构设计师

论文真题 模型驱动架构设计是一种用于应用系统开发的软件设计方法,以模型构造、模型转换和精化为核心,提供了一套软件设计的指导规范。在模型驱动架构环境下,通过创建出机器可读和高度抽象的模型实现对不同问题域的描述,这些模型独立于实现技术,以标准化的方式储存,利用…

Docker(九)-Docker运行redis6.0.8容器实例

1.宿主机新建目录存放redis.conf文件 目的&#xff1a;运行redis容器实例时使用自己的配置文件2.运行redis容器实例 docker run -d -p 6379:6379 --privilegedtrue -v 【宿主机配置文件目录】:/etc/redis/redis.conf -v 【宿主机数据目录】:/data --nameredis6.0.8 redis:6.0…

从挑战到实战!TDengine 新能源行业研讨会要点回顾

近年来&#xff0c;随着全球对可再生能源需求的不断增长&#xff0c;新能源行业迎来了前所未有的发展机遇。然而&#xff0c;伴随着行业的快速发展&#xff0c;海量数据的管理和高效利用成为了行业面临的重要挑战。如何通过先进的数据管理技术提升新能源系统的效率和可靠性&…

安科瑞智能物联网远传电表的优势

物联网远传智能电表是一种新型的电表&#xff0c;它通过物联网技术实现了电能的远程监测和管理。下面是物联网远传智能电表的优缺点&#xff1a;王盼盼&#xff1b;18721098782/Acrel 优点&#xff1a; 1. 实现了电能的远程监测和管理&#xff0c;可以随时随地了解电能的使用…

烟火监测报警摄像机

当今社会&#xff0c;随着城市化进程的加快和人们生活水平的提高&#xff0c;烟火监测报警摄像机作为一种新型智能安防设备&#xff0c;正逐步在各个领域得到广泛应用&#xff0c;其在保障公共安全和预防火灾中的作用日益凸显。烟火监测报警摄像机利用先进的视觉识别技术和智能…

基于FreeRTOS+STM32CubeMX+LCD1602+MCP3304(SPI接口)的8通道ADC转换器Proteus仿真

一、仿真原理图: 二、仿真效果: 三、STM32CubeMX配置: 1)、GPIO配置: 2)、SPI接口配置: 三、软件代码: 1)、GPIO初始化: static void MX_GPIO_Init(void) { GPIO_InitTypeDef GPIO_InitStruct = {0}; /* GPIO Ports Clock Enable */ __HAL_RCC_GPIOC_CLK_ENABL…

Markdown快速生成PPT!用这2个AI办公软件就够了!

对于喜欢使用Markdown编辑内容的人来说&#xff0c;经常会遇到诸如此类的需求&#xff1a; Markdown怎么生成PPT&#xff1f;Markdown怎么生成思维导图&#xff1f;Markdown怎么生成Word&#xff0c;或是导出为Word&#xff1f;Markdown怎么生成表格&#xff1f;…… 使用了比…

如何正确关闭偶发的安卓App闪退Bug?

对于测试遇到偶现的Bug&#xff0c;很多软件测试工程师的做法都是尽量的复现问题的步骤&#xff0c;在复现的过程&#xff0c;有的是录制视频或者打开日志&#xff0c;以便复现的时候&#xff0c;能捕捉到&#xff0c;定位问题&#xff0c;最后如果没有&#xff0c;那就先把问题…

RabbitMQ的Fanout交换机

Fanout交换机 Fanout&#xff0c;英文翻译是扇出&#xff0c;我觉得在MQ中叫广播更合适。 在广播模式下&#xff0c;消息发送流程是这样的&#xff1a; 1&#xff09; 可以有多个队列2&#xff09; 每个队列都要绑定到Exchange&#xff08;交换机&#xff09;3&#xff09; …

嵌入式学习——硬件(ARM内核汇编指令)——day52

ARM汇编指令 学习arm汇编的主要目的是为了编写arm启动代码&#xff0c;启动代码启动以后&#xff0c;引导程序到c语言环境下运行。换句话说启动代码的目的是为了在处理器复位以后搭建c语言最基本的需求。因此启动代码的主要任务有&#xff1a; 初始化异常向量表&#xff1b;初…

linux进程是什么?

进程概念 进程Process是指计算机中已运行的程序&#xff0c;是系统进行资源分配和调度的基本单位&#xff0c;是操作系统结构的基础。 在早期面向进程设计的计算机结构中&#xff0c;进程是程序的基本执行实体。在当代面向线程设计的计算机结构中&#xff0c;进程是线程的容器…

Spring Cloud Netflix:构建强大微服务生态系统的利器

Spring Cloud Netflix是一组集成框架&#xff0c;它将Netflix的多个开源组件整合到Spring Boot应用程序中&#xff0c;使得构建云原生应用程序变得更加简单。这些组件包括用于服务发现和注册的Eureka&#xff0c;断路器模式的实现Hystrix&#xff0c;用于API网关的Zuul&#xf…

零点到两点,我部署了一个es

一开始的准备 实在是水平有限&#xff0c;Clash虚拟机网出不去&#xff0c;研究了LAN方案&#xff0c;还在咸鱼买了一单&#xff0c;搞不定&#xff0c;没辙&#xff0c;那我老老实实下载tar包得了&#xff0c;就不docker了 下载安装 直接官网给它安个es https://www.elasti…