MapStruct使用方法

一、用途

1.1 优势

与动态映射框架相比,MapStruct 具有以下优势:
(1)通过使用普通方法getter、setter调用,而不是反射来快速执行,效率很高。
(2)编译时类型安全:只能映射相互映射的对象和属性,不会将其余模型属性进行映射。

二、依赖


<dependency>
    <groupId>org.mapstructgroupId>
    <artifactId>mapstructartifactId>
    <version>1.4.2.Finalversion>
dependency>

三、使用详解-常规映射

3.1 基本映射(属性相同)

创建映射接口:

//spring: 生成的实现类上面会自动添加一个@Component注解,可以通过Spring的 @Autowired方式进行注入
//default: 这是默认的情况,mapstruct 不使用任何组件类型, 可以通过Mappers.getMapper(Class)方式获取自动生成的实例对象。
@Mapper(componentModel = "spring")
public interface UserAssembler {

    UserAssembler INSTANCE = Mappers.getMapper(UserAssembler.class);

    UserDTO toUserDTO(User user);

    User toUser(UserDTO userDTO);
}

注意:

上述代码中,UserAssembler INSTANCE = Mappers.getMapper(UserAssembler.class); 有什么作用?
——前面我们在Mapper接口中代码中一直有一行代码,如下所示,是MapStruct为我们提供的映射工厂,指定接口类型后自动帮我们创建接口的实现,且保证是线程安全的单例,无需自己手动创建。
例如,在以下代码中,我们创建了UserMapper的单例实例,并使用它转换两个不同的User对象:

 但是,某些时候尤其是在做项目时,我们用到了Sping,希望映射后的新实例是交给Spring管理。这时候就需要进行依赖注入了。只需要在Mapper接口中的@Mapper注解中加入componentModel = "spring"即可

UserAssembler  userAssembler = UserAssembler.INSTANCE;

User user1 = new User();
user1.setId(1);
user1.setName("Alice");

User user2 = new User();
user2.setId(2);
user2.setName("Bob");

UserDTO userDTO1 = userAssembler.toUserDTO(user1);
UserDTO userDTO2 = userAssembler.toUserDTO(user2);

通过UserAssembler.INSTANCE获取UUserAssembler单例实例,我们可以在任何时候重复使用这个实例来转换不同的User对象。

 需要注意的是,为了能够成功生成映射代码,映射接口必须使用@Mapper注解进行标识。同时,MapStruct也支持在映射接口中定义多个映射方法,每个方法使用不同的@Mapping注解来描述源对象和目标对象之间的映射关系。

 总结来说,MapStruct中的Mappers.getMapper()方法可以获得映射接口的实例,UserAssembler INSTANCE = Mappers.getMapper(UserAssembler.class);则是创建了一个静态字段用于存储UserMapper单例实例的引用,方便在程序中反复使用。

3.2 基本映射(属性不同)

直接给方法了:

@Mappings({
    @Mapping(source = "id", target = "userId"),
    @Mapping(source = "name", target = "userName")
    })
    UserVO4 toConvertVO(User source);

3.3 表达式

	@Mappings({
      @Mapping(target = "createTime", expression = 	"java(com.java.mmzsblog.util.DateTransform.strToDate(source.getCreateTime()))"),
    })
    UserVO3 toConvertVO3(User source);

    User fromConvertEntity3(UserVO3 userVO3);

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

@Mapper
public interface UserAssembler {

    UserAssembler INSTANCE = Mappers.getMapper(UserAssembler.class);

    @Mappings({
        @Mapping(source = "user.name", target = "fullName"),
        @Mapping(source = "address.street", target = "streetName"),
        // 更多映射关系...
    })
    UserDTO toUserDTO(User user, Address address);
}

3.5 将嵌套的 bean 属性映射到当前目标

参考3.4:

@Mapper
public interface UserAssembler {
    UserAssembler INSTANCE = Mappers.getMapper(UserAssembler.class);

    @Mapping(source = "address.street", target = "streetName")
    UserDTO toUserDTO(User user);

    AddressDTO toAddressDTO(Address address);
}

3.4 Map到Bean的映射

@Mapper
public interface UserAssembler {
    UserAssembler INSTANCE = Mappers.getMapper(UserAssembler.class);

    @Mapping(target = "name", source = "map.name")
    @Mapping(target = "age", source = "map.age")
    User mapToUser(Map<String, Object> map);
}

四、使用详解-数据类型转化

4.1 类型转换

4.1.1 使用自定义方法:

@Mapper
public interface UserAssembler {
    UserAssembler INSTANCE = Mappers.getMapper(UserAssembler .class);

    @Mapping(target = "age", source = "dto.age")
    User toUser(UserDTO dto);

    default LocalDate mapToLocalDate(String dateStr) {
        return LocalDate.parse(dateStr);
    }
}

 在上述示例中,我们定义了一个mapToLocalDate方法,该方法将接收一个String类型的参数dateStr并返回一个LocalDate类型的结果。在toUser方法中,我们使用@Mapping注解指定将dto.age映射到User对象的age属性。MapStruct会自动调用mapToLocalDate方法进行数据类型转换。

4.1.2 使用内置的转换器:

 MapStruct提供了一些内置的转换器,用于常见的类型转换。例如,String到LocalDate、String到BigDecimal等。你可以直接在映射方法上使用这些转换器,而无需编写额外的代码。

@Mapper
public interface UserAssembler{
    UserAssemblerINSTANCE = Mappers.getMapper(UserAssembler.class);

    @Mapping(target = "birthDate", source = "dto.birthDate", dateFormat = "yyyy-MM-dd")
    User toUser(UserDTO dto);
}

 在上述示例中,我们使用dateFormat属性指定了dto.birthDate字符串的日期格式,MapStruct会根据指定的格式将其转换为User对象的birthDate属性类型。

 这只是两种常见的方式,MapStruct还提供了更多高级的自定义转换选项,例如使用自定义的转换器类、使用注解标记转换器等。你可以根据具体的需求选择适合的方式进行数据类型转换。

 总结来说,MapStruct提供了多种方式来进行数据类型转换,包括自定义方法和使用内置的转换器。通过灵活运用这些转换选项,你可以轻松地处理不同类型之间的映射和转换。

4.2 隐式类型转化

 在MapStruct中,隐式类型转换指的是在映射过程中,不需要显式地指定数据类型转换,而是根据数据类型自动进行转换。
例如,在以下示例中,我们定义了一个User对象和一个UserDTO对象,并使用MapStruct将它们进行映射:

@Mapper
public interface UserAssembler{
    UserAssembler INSTANCE = Mappers.getMapper(UserAssembler.class);

    UserDTO toUserDTO(User user);
}

 在上述示例中,我们并没有指定具体的字段映射规则,而是通过方法名匹配来自动映射User对象和UserDTO对象的属性,这就是MapStruct中的隐式类型转换。在默认情况下,如果两个属性的名称和类型都相同,则会自动进行数据的映射。

 当然,如果属性名称或数据类型不匹配,则需要通过@Mapping注解或自定义方法来指定映射规则。例如,在以下示例中,User对象的birthDate属性类型为Date,而UserDTO对象的birthDate属性类型为String,因此我们需要指定类型转换规则:

@Mapper
public interface UserAssembler{
    UserAssembler INSTANCE = Mappers.getMapper(UserAssembler.class);

    @Mapping(target = "birthDate", source = "user.birthDate", dateFormat = "yyyy-MM-dd")
    UserDTO toUserDTO(User user);
}

 在上述示例中,我们使用@Mapping注解指定了将User对象的birthDate属性转换为字符串类型,并指定了日期格式为yyyy-MM-dd。

 总之,MapStruct支持隐式类型转换,可以根据属性名称和数据类型自动进行数据映射。当然,在需要进行类型转换时,你也可以通过@Mapping注解或自定义方法来指定映射规则。

4.3 从int到String的转换

@Mapper
public interface UserAssembler{
    UserAssembler INSTANCE = Mappers.getMapper(UserAssembler.class);

    @Mapping(target = "ageStr", expression = "java(String.valueOf(user.getAge()))")
    UserDTO toUserDTO(User user);
}

或者

@Mapper
public interface CarMapper {

    @Mapping(source = "price", numberFormat = "$#.00")
    CarDto carToCarDto(Car car);

    @IterableMapping(numberFormat = "$#.00")
    List<String> prices(List<Integer> prices);
}

4.3 从 BigDecimal 到 String 的转换

@Mapper
public interface UserAssembler{
    UserAssembler INSTANCE = Mappers.getMapper(UserAssembler.class);

    @Mapping(target = "salaryStr", expression = "java(user.getSalary().toString())")
    UserDTO toUserDTO(User user);
}

 在上述示例中,我们使用expression属性指定了将user.getSalary()的结果转换为String类型,并映射到UserDTO对象的salaryStr属性。

或者

@Mapper
public interface CarMapper {

    @Mapping(source = "power", numberFormat = "#.##E0")
    CarDto carToCarDto(Car car);

}

4.4 从日期到字符串的转换

@Mapper
public interface UserAssembler{
    UserAssembler INSTANCE = Mappers.getMapper(UserAssembler.class);

    @Mapping(target = "birthDateStr", dateFormat = "yyyy-MM-dd")
    UserDTO toUserDTO(User user);
}

 在上述示例中,我们使用dateFormat属性指定了将User对象的birthDate属性转换为String类型,并指定了日期格式为yyyy-MM-dd。MapStruct会自动进行日期转换并将结果映射到UserDTO对象的birthDateStr属性。

五、使用详解-多个对象,源数据来源普通参数

@Mapper
public interface AddressAssembler {

    @Mapping(target = "description", source = "person.description")
    @Mapping(target = "houseNumber", source = "hn")
    DeliveryAddressDto personAndAddressToDeliveryAddressDto(Person person, Integer hn);
}

六、条件映射

 条件映射属于一种 源存在性检测,不同于默认的调用 XYZ 属性的 hasXYZ 方法进行存在性检测,条件映射允许自定义检测方法,来决定是否对属性进行映射。

 在方法上使用 org.mapstruct.Condition 注解并返回 boolean 类型值的方法,即为自定义的条件检测方法。

例如,您只想映射不为 null 且不是空的字符串属性时:

@Mapper
public interface CarMapper {
    CarDto carToCarDto(Car car);

    @Condition
    default boolean isNotEmpty(String value) {
        return value != null && !value.isEmpty();
    }
}

七、限定符结合默认值

使用限定符指定生成默认值方法。

  @Mapper
  public interface MovieMapper {

       @Mapping( target = "category", qualifiedByName = "CategoryToString", defaultValue = "DEFAULT" )
       GermanRelease toGerman( OriginalRelease movies );

       @Named("CategoryToString")
       default String defaultValueForQualifier(Category cat) {
           // some mapping logic
       }
  }

此时 category 为空时,将使用 defaultValueForQualifier 方法生成默认值。

八、使用详解-集合映射

8.1 具有List映射方法的映射器

映射list,一般要在映射器里先定义好对应的对象映射器:

@Mapper
public interface ExampleMapper {
    
    ExampleMapper INSTANCE = Mappers.getMapper(ExampleMapper.class);
    
    TargetObject map(SourceObject sourceObject);
    
    List<TargetObject> mapList(List<SourceObject> sourceList);
}

8.2 Map集合映射

参见【3.4】

九、使用详解-值映射

9.1 将枚举映射到枚举类型

  MapStruct 中将一个枚举映射到另一个枚举类型,您可以使用 @Mapping 注解来指定枚举字段的映射规则。以下是一个示例:

@Mapper
public interface ExampleMapper {
    ExampleMapper INSTANCE = Mappers.getMapper(ExampleMapper.class);
    
    @Mapping(target = "targetEnum", source = "sourceEnum")
    TargetObject map(SourceObject sourceObject);
}

参考:
MapStruct的基础用法详解
MapStruct使用说明

你可能感兴趣的:(java)