MapStruct快速入门和实战

126 阅读4分钟

概述

场景

很多时候,会出现需要实体转换的情况,直接通过get和set方法,代码很冗余而且麻烦,所以就出现了实体类映射工具。比如通过BeanUtil,但是BeanUtil是基于反射实现,在运行时才会被发现,而且层层反射的性能差,灵活性也不怎么好。相比而言MapStruct就更加合适。

Mapstruct的优势:

  • 简化的映射配置:使用MapStruct,您只需要定义一个接口,并在接口中声明映射方法,无需编写繁琐的映射逻辑。MapStruct会根据方法的命名和参数类型自动生成映射代码。
  • 零依赖:MapStruct是一个独立的库,不依赖于其他第三方库。
  • 支持多种映射策略:MapStruct支持多种映射策略,包括属性名相同的映射、自定义映射方法、基于注解的映射等。您可以根据需要选择适合的映射策略。
  • 易于扩展和定制:MapStruct提供了丰富的扩展点和配置选项,可以根据具体需求进行定制。您可以自定义映射逻辑、添加转换器、配置映射策略等。
  • 支持嵌套映射和集合映射:MapStruct支持嵌套对象之间的映射,以及集合对象之间的映射。
  • 编译时类型检查。

常见参数以及使用场景

用于在自动生成的映射器代码中指定属性之间的映射关系,允许你自定义如何映射源对象的属性到目标对象的属性。

参数说明:

souce、target

在源对象和目标对象之间存在不同命名或复杂映射关系时,可以指定如何映射。例如:

@Mapper
public interface MyMapper {
    @Mapping(source = "sourceName", target = "targetName")
    TargetObject map(SourceObject source);
}

在上面的例子中,@Mapping注解指定了源对象的sourceName属性应该映射到目标对象的targetName属性。

对于嵌套的bean

@Mapper
public interface FishTankMapper {
    @Mapping(target = "fish.kind", source = "fish.type")
    FishTankDto map( FishTank source );
}

dateFormat

用于指定日期属性的格式化方式

@Mapper
public interface MyMapper {
    @Mapping(source = "dateOfBirth", target = "birthDate", dateFormat = "yyyy-MM-dd")
    TargetObject map(SourceObject source);
}

numberFormat

用于指定数字的格式化方式

@Mapper
public interface CarMapper {
 
    @Mapping(source = "price", numberFormat = "$#.00")
    CarDto carToCarDto(Car car);
}

source对应的字段要符合numberFormat的要求

constant:

用于将属性映射为常量值,如@Mapping(target = "status", constant = "ACTIVE")。

expression

允许你为源和目标属性之间的映射提供自定义的Java表达式。

@Mapper
public interface SourceTargetMapper {
    @Mapping(target = "timeAndFormat",
         expression = "java( new org.sample.TimeAndFormat( s.getTime(), s.getFormat() ) )")
    Target sourceToTarget(Source s);
}

defaultExpression

defaultExpression属性的值应该是一个字符串,这个字符串应该是一个有效的SpEL(Spring Expression Language)表达式。

它允许你提供一个默认的表达式,该表达式在源属性值为null时指定

imports java.util.UUID;
 
@Mapper( imports = UUID.class )
public interface SourceTargetMapper {
    SourceTargetMapper INSTANCE = Mappers.getMapper( SourceTargetMapper.class );
 
    @Mapping(target="id", source="sourceId", defaultExpression = "java( UUID.randomUUID().toString() )")
    Target sourceToTarget(Source s);
}

ignore

用于指示在映射过程中是否忽略特定的属性。这个属性可以设置为true或false

默认为false,表示不忽略目标属性,映射器会尝试将源属性映射到目标属性。为true,映射器将不会尝试将源属性映射到目标属性。

@Mapper
public interface MyMapper {
    @Mapping(source = "sourceAge", target = "targetAge", ignore = true)
    TargetObject mapWithAgeIgnored(SourceObject source);
}

qualifiedBy、qualifiedByName

用于指定映射处理的自定义方法。

qualifiedBy属性接受一个类型为java.lang.Class<?>的参数,用于指定一个实现了指定接口的类,该类中定义了自定义的映射方法。

qualifiedByName属性接受一个字符串作为参数,用于指定一个具有特定名称的方法。这个方法应该定义在实体类之间映射的转换逻辑。

@Mapper(component = "mapper")  
public interface MyMapper {  
  @Mapping(source = "age", target = "age", qualifiedBy = MyCustomMapper.class)  
  Person person2PersonDTO(Person person);  
}
@Mapper(component = "mapper")  
public interface MyMapper {  
  @Mapping(source = "age", target = "age", qualifiedByName = "convertAge")  
  Person person2PersonDTO(Person person);  
}

dependsOn

用于指定其他注解的映射,这些注解依赖于当前注解的映射。

@Mapper  
public interface UserMapper {  
  
    @Mapping(target = "id", source = "user.id")  
    @Mapping(target = "name", source = "user.name", dependsOn = {"repositoryName"})  
    UserDTO userToUserDTO(User user);  
}

指定了name字段依赖于repositoryName字段的映射。这意味着在将User转换为UserDTO时,MapStruct会首先处理repositoryName字段的映射,然后再处理name字段的映射。

defaultValue

它允许你为映射的字段指定一个默认值。当源对象中的字段值为null或未定义时,会使用默认值映射给目标字段

快速入门-代码demo

依赖导入

<dependency>
  <groupId>org.mapstruct</groupId>
  <artifactId>mapstruct</artifactId>
  <version>1.4.2.Final</version>
</dependency>
<dependency>
  <groupId>org.mapstruct</groupId>
  <artifactId>mapstruct-processor</artifactId>
  <version>1.4.2.Final</version>
</dependency>
<dependency>
  <groupId>org.projectlombok</groupId>
  <artifactId>lombok</artifactId>
  <version>1.18.30</version>
  <scope>provided</scope>
</dependency>

实体类

@Data
public class UserDto {
    private String name;
    private Integer age;
    private String gender;
    private Integer userId;
    private Integer password;
}
@Data
public class User {
    private String name;
    private Integer userId;
    private Integer password;
}

映射

@Mapper
public interface UserMapper {
    UserMapper INSTANCE = Mappers.getMapper(UserMapper.class);
    UserDto toDto(User user);
    User toEntity(UserDto userDto);
}

测试类

@Test
void contextLoads() {
    UserDto userDto=new UserDto();
    userDto.setName("xgss");
    userDto.setUserId(123);
    userDto.setPassword(666);
    User user = UserMapper.INSTANCE.toEntity(userDto);
    System.out.println(user);
}

测试结果: