mapstruct使用教程

概述

针对模型转换,我们常用的方式是
方式一:BeanUtils.copyProperties(source, target);
方式二:自己写converter方法。

下面讲一个目前主流推荐的做法:使用mapstruct
mapstruct本质就是自动帮我们生成转换代码。我们只需要配置好策略即可。

依赖

...
<properties>
    <org.mapstruct.version>1.5.3.Final</org.mapstruct.version>
</properties>
...
<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>
		            <!-- 注意顺序,lombok在前,mapstruct在后 -->
		            <path>
		                <groupId>org.projectlombok</groupId>
		                <artifactId>lombok</artifactId>
		                <version>1.18.24</version>
		            </path>
		            <path>
		                <groupId>org.mapstruct</groupId>
		                <artifactId>mapstruct-processor</artifactId>
		                <version>${org.mapstruct.version}</version>
		            </path>
		        </annotationProcessorPaths>
		    </configuration>
		</plugin>
    </plugins>
</build>

定义mapper

为了告诉mapstruct怎么生成转换代码,我们需要先定义个接口。官方把这个操作称为:定义mapper。(创建映射器)

@Mapper
public interface CarMapper {

    CarMapper INSTANCE = Mappers.getMapper(CarMapper.class);
    // 模型转换
    UnityDTO modelToDTO(UnityModel unityModel);
}

两个模型如下:

@Data
public class UnityDTO {

    /**
     * 纠纷单Id
     */
    private String disputeOrderId;

    /**
     * 订单Id
     */
    private Long orderId;
}

@Data
public class UnityModel {

    /**
     * 纠纷单Id
     */
    private String disputeOrderId;

    /**
     * 订单Id
     */
    private Long orderId;
}

这个时候只需要,编辑下项目,mapstruct就会帮我们把转换代码生成出来。生成的代码在target/classes文件夹里。

如果是简单使用,理论上来说,其实已经结束了。不过只是简单的使用肯定满足不了我们的需求。

字段名不一致的情况

简单情况

如果两个字段名不一致,如下,一个orderId,一个是oId。

@Data
public class UnityDTO {

    /**
     * 纠纷单Id
     */
    private String disputeOrderId;

    /**
     * 订单Id
     */
    private Long orderId;
}

@Data
public class UnityModel {

    /**
     * 纠纷单Id
     */
    private String disputeOrderId;

    /**
     * 订单Id
     */
    private Long oId;
}

指定映射字段:

@Mapper
public interface CarMapper {

    CarMapper INSTANCE = Mappers.getMapper(CarMapper.class);
    // 模型转换
    @Mapping(target = "orderId", source = "oId")
    UnityDTO modelToDTO(UnityModel unityModel);
}

复杂情况

expression

下面情况是:model里面平铺的字段,转换到DTO中时,希望能放到extInfo的map中:

public class UnityModel {

    private String reverseBizType;

    private String reverseOrderBizType;
}

public class UnityDTO {

    /**
     * 纠纷单Id
     */
    private Map<String, String> extInfo;
}
/**
 * 字段不同时的指定方式,多个字段不同,写多个@Mapping即可
 */
@Mappings({
        @Mapping(target = "extInfo", expression = "java(INSTANCE.fillExtInfo(unityModel))")
})
UnityDTO modelToDTO(UnityModel unityModel);

default Map<String, String> fillExtInfo(JudgementUnityModel unityModel) {
    Map<String, String> map = new HashMap<>();
    Map<String, String> extInfo = unityModel.getExtInfo();
    if (extInfo != null) {
        map.putAll(extInfo);
    }
    map.put("reverseBizType", unityModel.getReverseBizType());
    map.put("reverseOrderBizType", unityModel.getReverseOrderBizType());
    return map;
}

这里是利用expression的能力,单独调用fillExtInfo方法。
具体步骤:

  1. 使用@Mapping(target = "目标字段名", expression = "表达式")
  2. 编写表达式中的方法,入参为source模型。

afterMapping

除了使用expression的方式之外,还可以使用@AfterMapping

@AfterMapping
default void fillExtInfo(@MappingTarget JudgementUnityMqModel unityMqModel, JudgementUnityModel unityModel){
    Map<String, String> map = fillExtInfo(unityModel);
    unityMqModel.setExtInfo(map);
}

mapstruct生成的方法后,fillExtInfo会放在方法的最后。

说明:

  1. @MappingTarget用来指定需要更新的对象。或者说,假设我们不想new一个对象,就可以使用@MappingTarget进行指定。
  2. @AfterMapping作用于生成方法的尾部,并且作用于相同映射关系的全部方法。

具有多个源参数的映射方法

@Mapper
public interface AddressMapper {
    @Mapping(target = "description", source = "person.description")
    @Mapping(target = "houseNumber", source = "address.houseNo")
    DeliveryAddressDto personAndAddressToDeliveryAddressDto(Person person, Address address);
}

入参为null的情况下,希望返回空对象

例如:

List<JudgementModel> resToModelList(List<LiabilityResponse> res);

默认情况下,入参:res为null时,返回的也是null。
这种情况下,可以添加:

//策略
@IterableMapping(nullValueMappingStrategy = NullValueMappingStrategy.RETURN_DEFAULT)
List<JudgementModel> resToModelList(List<LiabilityResponse> res);

总共有:@BeanMapping、@IterableMapping、@MapMapping 或全局@Mapper 或@MapperConfig,可以更改映射结果以返回空默认值。这意味着:

官方说明:https://mapstruct.org/documentation/stable/reference/html/#mapping-result-for-null-arguments

没生效的情况

对象模型继承父类字段

父类字段没有生成 setXXX代码。

多半是因为lombokmapstruct 先后顺序导致的:

<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>
           <!-- 注意顺序,lombok在前,mapstruct在后 -->
            <path>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <version>1.18.24</version>
            </path>
            <path>
                <groupId>org.mapstruct</groupId>
                <artifactId>mapstruct-processor</artifactId>
                <version>${org.mapstruct.version}</version>
            </path>
        </annotationProcessorPaths>
    </configuration>
</plugin>

总结

目前常用的:

  1. @Mapping 对字段一一指定
  2. 使用express指定转换方法
  3. 使用@afterMapping是放到方法的尾部。当然也有@beforeMapping,它有点特点,如果没有@MappingTarget,那么会在创建bean之前调用,如果有,就是创建bean之后调用。具体参考官方文档。

参考地址:

mapstruct官方文档

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

山鬼谣me

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

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

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

打赏作者

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

抵扣说明:

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

余额充值