美文网首页
MapStruct使用指南

MapStruct使用指南

作者: Tina_Xu | 来源:发表于2023-07-05 17:57 被阅读0次

介绍

大多数时候,终端用户或服务不需要访问模型中的全部数据,而只需要访问某些特定的部分。
数据传输对象(Data Transfer Objects, DTO)经常被用于这些应用中。DTO只是持有另一个对象中被请求的信息的对象。通常情况下,这些信息是有限的一部分。例如,在持久化层定义的实体和发往客户端的DTO之间经常会出现相互之间的转换。由于DTO是原始对象的反映,因此这些类之间的映射器在转换过程中扮演着关键角色。
这就是MapStruct解决的问题:手动创建bean映射器非常耗时。 但是该库可以自动生成Bean映射器类。

基本映射

public class Doctor {
    private int id;
    private String name;
    // getters and setters or builder
}
public class DoctorDto {
    private int id;
    private String name;
    // getters and setters or builder
}
@Mapper
public interface DoctorMapper {
    DoctorMapper INSTANCE = Mappers.getMapper(DoctorMapper.class);
    DoctorDto toDto(Doctor doctor);
}

不同属性名称映射

public class Doctor {
    private int id;
    private String name;
    private String specialty;
    // getters and setters or builder
}
public class DoctorDto {
    private int id;
    private String name;
    private String specialization;
    // getters and setters or builder
}
@Mapper
public interface DoctorMapper {
    DoctorMapper INSTANCE = Mappers.getMapper(DoctorMapper.class);

    @Mapping(source = "doctor.specialty", target = "specialization")
    DoctorDto toDto(Doctor doctor);
}

多个源类映射

public class Education {
    private String degreeName;
    private String institute;
    private Integer yearOfPassing;
    // getters and setters or builder
}
public class DoctorDto {
    private int id;
    private String name;
    private String degree;
    private String specialization;
    // getters and setters or builder
}
@Mapper
public interface DoctorMapper {
    DoctorMapper INSTANCE = Mappers.getMapper(DoctorMapper.class);

    @Mapping(source = "doctor.specialty", target = "specialization")
    @Mapping(source = "education.degreeName", target = "degree")
    DoctorDto toDto(Doctor doctor, Education education);
}

子对象映射

public class Patient {
    private int id;
    private String name;
    // getters and setters or builder
}
public class Doctor {
    private int id;
    private String name;
    private String specialty;
    private List<Patient> patientList;
    // getters and setters or builder
}
public class PatientDto {
    private int id;
    private String name;
    // getters and setters or builder
}
public class DoctorDto {
    private int id;
    private String name;
    private String degree;
    private String specialization;
    private List<PatientDto> patientDtoList;
    // getters and setters or builder
}
@Mapper
public interface PatientMapper {
    PatientMapper INSTANCE = Mappers.getMapper(PatientMapper.class);
    PatientDto toDto(Patient patient);
}
@Mapper(uses = {PatientMapper.class})
public interface DoctorMapper {

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

    @Mapping(source = "doctor.patientList", target = "patientDtoList")
    @Mapping(source = "doctor.specialty", target = "specialization")
    DoctorDto toDto(Doctor doctor);
}

更新现有实例

希望用DTO的最新值更新一个模型中的属性,对目标对象(我们的例子中是DoctorDto)使用@MappingTarget注解,就可以更新现有的实例

@Mapper(uses = {PatientMapper.class})
public interface DoctorMapper {

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

    @Mapping(source = "doctorDto.patientDtoList", target = "patientList")
    @Mapping(source = "doctorDto.specialization", target = "specialty")
    void updateModel(DoctorDto doctorDto, @MappingTarget Doctor doctor);
}

数据类型映射

@Mapper
public interface PatientMapper {
    @Mapping(source = "dateOfBirth", target = "dateOfBirth", dateFormat = "dd/MMM/yyyy")
    Patient toModel(PatientDto patientDto);
}
// 数字格式转换示例
@Mapping(source = "price", target = "price", numberFormat = "$#.00")

枚举映射

public enum PaymentType {
    CASH,
    CHEQUE,
    CARD_VISA,
    CARD_MASTER,
    CARD_CREDIT
}
public enum PaymentTypeView {
    CASH,
    CHEQUE,
    CARD
}
@Mapper
public interface PaymentTypeMapper {

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

    @ValueMappings({
            @ValueMapping(source = "CARD_VISA", target = "CARD"),
            @ValueMapping(source = "CARD_MASTER", target = "CARD"),
            @ValueMapping(source = "CARD_CREDIT", target = "CARD")
    })
    PaymentTypeView paymentTypeToPaymentTypeView(PaymentType paymentType);
}

依赖注入

@Mapper(componentModel = "spring")
public interface DoctorMapper {}

在@Mapper注解中添加(componentModel = "spring"),是为了告诉MapStruct,在生成映射器实现类时,我们希望它能支持通过Spring的依赖注入来创建。现在,就不需要在接口中添加 INSTANCE 字段了。
这次生成的 DoctorMapperImpl 会带有 @Component 注解:

@Component
public class DoctorMapperImpl implements DoctorMapper {}

只要被标记为@Component,Spring就可以把它作为一个bean来处理,你就可以在其它类(如控制器)中通过@Autowire注解来使用它:

@Controller
public class DoctorController() {
    @Autowired
    private DoctorMapper doctorMapper;
}

添加默认值

@Mapping 注解有两个很实用的标志就是常量 constant 和默认值 defaultValue 。无论source如何取值,都将始终使用常量值; 如果source取值为null,则会使用默认值。
修改一下 DoctorMapper ,添加一个 constant 和一个 defaultValue :

@Mapper(uses = {PatientMapper.class}, componentModel = "spring")
public interface DoctorMapper {
    @Mapping(target = "id", constant = "-1")
    @Mapping(source = "doctor.patientList", target = "patientDtoList")
    @Mapping(source = "doctor.specialty", target = "specialization", defaultValue = "Information Not Available")
    DoctorDto toDto(Doctor doctor);
}

添加表达式

MapStruct甚至允许在@Mapping注解中输入Java表达式。你可以设置 defaultExpression ( source 取值为 null时生效),或者一个expression(类似常量,永久生效)。
在 Doctor 和 DoctorDto两个类中都加了两个新属性,一个是 String 类型的 externalId ,另一个是LocalDateTime类型的 appointment ,两个类大致如下:

public class Doctor {

    private int id;
    private String name;
    private String externalId;
    private String specialty;
    private LocalDateTime availability;
    private List<Patient> patientList;
    // getters and setters or builder
}
public class DoctorDto {

    private int id;
    private String name;
    private String externalId;
    private String specialization;
    private LocalDateTime availability;
    private List<PatientDto> patientDtoList;
    // getters and setters or builder
}
@Mapper(uses = {PatientMapper.class}, componentModel = "spring", imports = {LocalDateTime.class, UUID.class})
public interface DoctorMapper {

    @Mapping(target = "externalId", expression = "java(UUID.randomUUID().toString())")
    @Mapping(source = "doctor.availability", target = "availability", defaultExpression = "java(LocalDateTime.now())")
    @Mapping(source = "doctor.patientList", target = "patientDtoList")
    @Mapping(source = "doctor.specialty", target = "specialization")
    DoctorDto toDtoWithExpression(Doctor doctor);
}

可以看到,这里将 externalId的值设置为 java(UUID.randomUUID().toString()) ,如果源对象中没有 availability 属性,则会把目标对象中的 availability 设置为一个新的 LocalDateTime对象。
由于表达式只是字符串,我们必须在表达式中指定使用的类。但是这里的表达式并不是最终执行的代码,只是一个字母的文本值。因此,我们要在 @Mapper 中添加 imports = {LocalDateTime.class, UUID.class} 。

@BeforeMapping 和 @AfterMapping

为了进一步控制和定制化,我们可以定义 @BeforeMapping 和 @AfterMapping方法。显然,这两个方法是在每次映射之前和之后执行的。也就是说,在最终的实现代码中,会在两个对象真正映射之前和之后添加并执行这两个方法。
可以在 DoctorCustomMapper中添加两个方法:

@Mapper(uses = {PatientMapper.class}, componentModel = "spring")
public abstract class DoctorCustomMapper {

    @BeforeMapping
    protected void validate(Doctor doctor) {
        if(doctor.getPatientList() == null){
            doctor.setPatientList(new ArrayList<>());
        }
    }

    @AfterMapping
    protected void updateResult(@MappingTarget DoctorDto doctorDto) {
        doctorDto.setName(doctorDto.getName().toUpperCase());
        doctorDto.setDegree(doctorDto.getDegree().toUpperCase());
        doctorDto.setSpecialization(doctorDto.getSpecialization().toUpperCase());
    }

    @Mapping(source = "doctor.patientList", target = "patientDtoList")
    @Mapping(source = "doctor.specialty", target = "specialization")
    public abstract DoctorDto toDoctorDto(Doctor doctor);
}

MapStruct使用指南

相关文章

  • mapstruct 和lombok 结合之后mapstruct生

    lombok和mapstruct配合转换bean后,mapstruct生成空的实现. 如果出现mapstruct和...

  • mapStruct使用

    https://mapstruct.org/

  • 简化mapstruct代码: mapstruct-spring-

    mapstruct MapStruct 是一个属性映射工具,只需要定义一个 Mapper 接口,MapStruc...

  • Java mapstruct生成实现

    1.注意:lombok和mapstruct一定要注意引用顺序,lombok一定要在mapstruct前面,不然生成...

  • 推荐一个神器,MapStruct,你用过吗?

    官网地址:http://mapstruct.org/ MapStruct是一个代码生成器,简化了不同的Java B...

  • MapStruct实现对象映射

    1 序 MapStruct是一个属性映射工具,只需要使用@Mapper注解标注的映射接口。MapStruct就会自...

  • MapStruct

    概述 MapStruct 可以将某几种类型的对象映射为另外一种类型,如将多个 DO(业务实体对象) 对象转换为 D...

  • MapStruct

    在mvc层经常会遇到这样的一种情况,是否使用DTO(数据传输对象),还是直接使用model返回?其实这都可以,前者...

  • MapStruct使用

    背景 在一个成熟可维护的工程中,细分模块后,domian工程最好不要被其他工程依赖,但是实体类一般存于domain...

  • MapStruct 使用

    对象映射工具的由来 大型项目采用分层开发,每层的数据模型都不同:在持久化层,模型层为 PO(Persistent ...

网友评论

      本文标题:MapStruct使用指南

      本文链接:https://www.haomeiwen.com/subject/ymmkudtx.html