使用MapStruct简化entity、dto、dxo之间的属性复制

官方文档
官方案例

MapStruct是一个Java注释处理器,用于生成类型安全的bean映射类。

您所要做的就是定义一个映射器接口,该接口声明任何所需的映射方法。在编译期间,MapStruct将生成此接口的实现。此实现使用纯Java方法调用来映射源对象和目标对象,即无反射或类似内容。

与手工编写映射代码相比,MapStruct通过生成繁琐且容易出错的代码来节省时间。MapStruct遵循一种约定而非配置的方法,它使用合理的默认值,但允许配置或实现特殊行为。

与动态映射框架相比,MapStruct具有以下优势:

  1. 通过使用普通方法调用(settter/getter)而不是反射来快速执行
  2. 编译时类型安全性:只能映射相互映射的对象和属性,不能将order实体意外映射到customer DTO等。
  3. 如果有如下问题,编译时会抛出异常
  • 映射不完整(并非所有目标属性都被映射)
  • 映射不正确(找不到正确的映射方法或类型转换)

工程配置,引入jar包

MapStruct和Lombok一起使用,所以必须同时引入配置。

下载插件:idea中下载 mapstruct support 插件,安装重启Idea

<properties>
    <org.mapstruct.version>1.5.3.Final</org.mapstruct.version>
    <org.projectlombok.version>1.18.24</org.projectlombok.version>
    <java.version>1.8</java.version>
</properties>
<dependencies>
    <!--mapStruct依赖 高性能对象映射-->
    <!--mapstruct核心-->
    <dependency>
        <groupId>org.mapstruct</groupId>
        <artifactId>mapstruct</artifactId>
        <version>${org.mapstruct.version}</version>
    </dependency>
    <!--mapstruct编译-->
    <dependency>
        <groupId>org.mapstruct</groupId>
        <artifactId>mapstruct-processor</artifactId>
        <version>${org.mapstruct.version}</version>
    </dependency>

    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>${org.projectlombok.version}</version>
        <optional>true</optional>
    </dependency>
</dependencies>
<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.8.1</version>
            <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>

                    <!-- additional annotation processor required as of Lombok 1.18.16 -->
                    <path>
                        <groupId>org.projectlombok</groupId>
                        <artifactId>lombok-mapstruct-binding</artifactId>
                        <version>0.2.0</version>
                    </path>
                </annotationProcessorPaths>
            </configuration>
        </plugin>
    </plugins>
</build>

一、简单使用

MapStruct通过一序列注解使用Java对象属性映射。你要做的是:

1、创建一个Bean和与之对应的Dto
2、创建一个接口(无须实现,MapStruct会自动生成实现类),使用@Mapper声明
3、在接口中定义转换的方法(方法名自己取),如果特殊属性映射可使用@Mapping声明,MapStruct会根据参数类型和返回值类型自动生成实现方法

1、创建一个Bean和与之对应的Dto

@Data
public class Person {
    
    
    String describe;
    private String id;
    private String name;
    private int age;
    private String fun;
    private BigDecimal money;
    private double height;
    private Date registerTime;
    private String createTime;

}

@Data
public class PersonDTO {
    
    
    String describe;
    private Long id;
    private String personName;
    private String age;
    private String money;
    private String height;
    private String registerTime;
    private Date createTime;
}

2、创建一个接口,在接口中定义转换的方法

import org.mapstruct.*;
import org.mapstruct.factory.Mappers;

@Mapper
public interface PersonMapper {
    
    
    PersonMapper INSTANCT = Mappers.getMapper(PersonMapper.class);

    PersonDTO toDto(Person person);
}

3、测试

@Test
public void test(){
    
    
    Person person = new Person();
    person.setDescribe("测试");
    person.setAge(18);
    person.setName("张三");
    person.setHeight(170.5);
    person.setMoney(new BigDecimal("1000000"));
    person.setCreateTime("2022-12-30");
    PersonDTO dto = PersonMapper.INSTANCT.toDto(person);
    System.out.println(dto);
}

二、个性化配置

// 属性名称映射
@Mapping(target = "personName", source = "name")
// 忽略属性,不进行映射
@Mapping(target = "id", ignore = true)
// 数字格式化
@Mapping(target = "money", numberFormat = "#,###.##")
// 日期格式化,转换成String
@Mapping(target = "createTime", dateFormat = "yyyy-MM-dd")
PersonDTO toDto(Person person);
// 默认值
@Mapping(target = "describe", defaultValue = "默认值")
// 赋值表达式
@Mapping(target = "createTime", expression = "java(new java.util.Date())")
PersonDTO toDto2(Person person);

// 组合映射
@Mapping(target = "createTime", source = "basicEntity.createTime")
PersonDTO combinationConver(Person person, BasicEntity basicEntity);

@InheritInverseConfiguration 反向操作

根据方法toDto的配置,方向赋值dto到entity,如果接口中只有一个配置方法,可以忽略name参数

@Mapper
public interface PersonMapper {
    
    
    PersonMapper INSTANCT = Mappers.getMapper(PersonMapper.class);
    
    // 属性名称映射
    @Mapping(target = "personName", source = "name")
    // 忽略属性,不进行映射
    @Mapping(target = "id", ignore = true)
    // 数字格式化
    @Mapping(target = "money", numberFormat = "#,###.##")
    // 日期格式化,转换成String
    @Mapping(target = "createTime", dateFormat = "yyyy-MM-dd")
    PersonDTO toDto(Person person);

    @InheritInverseConfiguration(name = "toDto")
    Person toEntity(PersonDTO dto);
}

@InheritConfiguration 集成配置

继承toDto的配置,又定义了一些自己的配置,如果接口中只有一个配置方法,可以忽略name参数

@Mapper
public interface PersonMapper {
    
    
    PersonMapper INSTANCT = Mappers.getMapper(PersonMapper.class);
    
    // 属性名称映射
    @Mapping(target = "personName", source = "name")
    // 忽略属性,不进行映射
    @Mapping(target = "id", ignore = true)
    // 数字格式化
    @Mapping(target = "money", numberFormat = "#,###.##")
    // 日期格式化,转换成String
    @Mapping(target = "createTime", dateFormat = "yyyy-MM-dd")
    PersonDTO toDto(Person person);

    // 默认值
    @Mapping(target = "describe", defaultValue = "默认值")
    // 赋值表达式
    @Mapping(target = "createTime", expression = "java(new java.util.Date())")
    // 继承配置
    @InheritConfiguration(name = "toDto")
    PersonDTO toDto2(Person person);
}

通过@Qualifier指定特定的转换方法

1、定义转换类,并用@Qualifier声明

public class DateFormatUtil {
    
    
    /** 使用 @Qualifier 指定自定义的注解*/
    @Qualifier
    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.CLASS)
    public @interface DateFormat {
    
    
    }

    /** 使用自定义的注解,声明转换方法,MapStruct会根据参数类型和返回值,自动匹配方法 */
    @DateFormat
    public static String dateToString(Date date) {
    
    
        return date == null ? "" : new SimpleDateFormat("yyyy-MM-dd").format(date);
    }

    /** 使用自定义的注解,声明转换方法,MapStruct会根据参数类型和返回值,自动匹配方法 */
    @DateFormat
    public static Date stringToDate(String date) throws ParseException {
    
    
        return date == null ? null : new SimpleDateFormat("yyyy-MM-dd").parse(date);
    }
}

2、在Mapper中使用

@Mapper(uses = {
    
    DateFormatUtil.class})
public interface PersonMapper {
    
    
    PersonMapper INSTANCT = Mappers.getMapper(PersonMapper.class);

    @Mapping(target = "registerTime", qualifiedBy = DateFormatUtil.DateFormat.class)
    @Mapping(target = "createTime", qualifiedBy = DateFormatUtil.DateFormat.class)
    PersonDTO toDto3(Person person);
}

自定义通用类型转换方法,以日期转换为例

1、创建转换工具类,MapStruct会根据参数类型和返回值,自动匹配方法

public class DateMapper {
    
    
    private SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");

    public String toString(Date date) {
    
    
        return date != null ? format.format(date) : null;
    }

    public Date toDate(String date) {
    
    
        try {
    
    
            return date != null ? format.parse(date) : null;
        } catch (ParseException e) {
    
    
            throw new RuntimeException(e);
        }
    }
}

2、在Mapper中使用

@Mapper(uses = {
    
    DateMapper.class})
public interface PersonMapper {
    
    
    PersonMapper INSTANCT = Mappers.getMapper(PersonMapper.class);

    PersonDTO toDto(Person person);
}

自动生成的实现类如下

public class PersonMapperImpl implements PersonMapper {
    
    
    private final DateMapper dateMapper = new DateMapper();

    public PersonMapperImpl() {
    
    
    }

    public PersonDTO toDto(Person person) {
    
    
        if (person == null) {
    
    
            return null;
        } else {
    
    
            PersonDTO personDTO = new PersonDTO();
            personDTO.setDescribe(person.getDescribe());
            if (person.getId() != null) {
    
    
                personDTO.setId(Long.parseLong(person.getId()));
            }

            personDTO.setAge(String.valueOf(person.getAge()));
            if (person.getMoney() != null) {
    
    
                personDTO.setMoney(person.getMoney().toString());
            }

            personDTO.setHeight(String.valueOf(person.getHeight()));
            personDTO.setRegisterTime(this.dateMapper.toString(person.getRegisterTime()));
            personDTO.setCreateTime(this.dateMapper.toDate(person.getCreateTime()));
            return personDTO;
        }
    }
}

优先级说明

以日期为例,转换方法优先级:@Qualifier > 自定义通用类型转换方法 > dateFormat参数

集成Spring环境使用

添加参数componentModel = MappingConstants.ComponentModel.SPRING,不需要再接口中添加代码PersonMapper INSTANCT = Mappers.getMapper(PersonMapper.class);

1、创建Mapper类,并添加参数componentModel

@Mapper(componentModel = MappingConstants.ComponentModel.SPRING, uses = DateMapper.class)
public interface Person2Mapper {
    
    

    @Mapping(target = "personName", source = "name")
    @Mapping(target = "id", ignore = true) // 忽略id,不进行映射
    PersonDTO toDto(Person person);

    // 根据指定配置进行逆映射
    @InheritInverseConfiguration(name = "toDto")
    Person toEntity(PersonDTO dto);
}

2、通过@Autowired直接使用

@Controller
public class TestController {
    
    
    @Autowired
    private PersonMapper personMapper;

    @RequestMapping("test1")
    @ResponseBody
    public PersonDTO test1(){
    
    
        Person person = new Person();
        person.setDescribe("测试");
        person.setAge(18);
        person.setName("张三");
        person.setHeight(170.5);
        person.setMoney(new BigDecimal("100"));
        person.setCreateTime("2022-12-30");
        PersonDTO dto = personMapper.toDto(person);
        return dto;
    }
}

猜你喜欢

转载自blog.csdn.net/wlddhj/article/details/128153094