MapStruct

发布于 2022-03-31  111 次阅读


1.什么是Mapper?

MapStruct是一个代码生成器,它基于convention over configuration(约定优于配置)的思想,极大地简化了 Java bean 类型之间转换。

1.1 为什么使用mapstruct?

JAVA开发按照传统的MVC或DDD模型开发时,各层传输之间的bean类型不同会涉及到大量的bean转换,比如: DO、BO、DTO、entity、VO等

现在有这么个场景,从数据库查询出来了一个user对象(包含id,用户名,密码,手机号,邮箱,角色这些字段)和一个对应的角色对象role(包含id,角色名,角色描述这些字段),现在在controller需要用到user对象的id,用户名,和角色对象的角色名三个属性。

一种方式是直接把两个对象传递到controller层,但是这样会多出很多没用的属性。更通用的方式是需要用到的属性封装成一个类(DTO),通过传输这个类的实例来完成数据传输。

1.2 使用mapstruct优点 与缺点分别是什么?

*优点:
mapstruct是在编译时完成Bean转换,与内存式(BeanUtils.copy…)对比,提升了服务性能。
mapstruct是在编译时完成Bean转换, 编译后在target目录,生成对应的xxximp.java文件,代码对开发人员透明,方便定位bug。

2.项目中如何引入mapstruct

   <properties>
        <lombok.version>1.18.16</lombok.version>
        <org.mapstruct.version>1.4.2.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>
                        <!-- other annotation processors -->
                        <path>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                            <version>${lombok.version}</version>
                        </path>
                        <path>
                            <groupId>org.mapstruct</groupId>
                            <artifactId>mapstruct-processor</artifactId>
                            <version>${org.mapstruct.version}</version>
                        </path>
                    </annotationProcessorPaths>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>


3 mapstruct如何使用(语法说明): 直接贴代码

3.1 方式1

/**
 * @Mapper 定义这是一个MapStruct对象属性转换接口,在这个类里面规定转换规则
 *          在项目构建时,会自动生成改接口的实现类,这个实现类将实现对象属性值复制
 */
@Mapper
public interface UserRoleMapper {
    /**
     * 获取该类自动生成的实现类的实例
     * 接口中的属性都是 public static final 的 方法都是public abstract     */
    UserRoleMapper INSTANCES = Mappers.getMapper(UserRoleMapper.class);

    /**
     * 这个方法就是用于实现对象属性复制的方法
     *
     * @Mapping 用来定义属性复制规则 source 指定源对象属性 target指定目标对象属性
     *
     * @param user 这个参数就是源对象,也就是需要被复制的对象
     * @return 返回的是目标对象,就是最终的结果对象
     */

    @Mappings({@Mapping(source = "id",target = "userId"),
               @Mapping(source = "username",target = "name"),
               @Mapping(source = "roleName",target = "role.roleName")})
    UserRoleDto toUserRoleDto(User user);

}

3.2 方式2

//将bean交给spring管理, --> componentModel = "spring"
//目标属性未匹配时系统忽略,不打印警告日志 -->unmappedTargetPolicy = ReportingPolicy.IGNORE
// 公共的转换组件引用 --> uses = {ConvertUtil.class}
@Mapper(unmappedTargetPolicy = ReportingPolicy.IGNORE, uses = {ConvertUtil.class}, componentModel = "spring")
public interface CarMapper {

// 转换函数的定义
@Mapping(source = "numberOfSeats", target = "seatCount")
CarDto carToCarDto(Car car); 2
}

3.3 @Mapper语法小样

// 示例小样1
@Mapper(unmappedTargetPolicy = ReportingPolicy.IGNORE, uses = ConvertUtil.class, componentModel = "spring")

// 指定默认值
@Mapping(target = "name", source="name", defaultValue = "我是默认值") 

// 使用java可执行代码或函数
@Mapping(target = "name", expression = "java(\"OT\" + paramBean.getId())")

// 使用常量代替默认值
@Mapping(target = "stringConstant", constant = "Constant Value")

//日期格式化
@Mapping(source = "birthday", target = "birthday", dateFormat = "yyyy-MM-dd HH:mm:ss")
// Integer 到 String的转换
@Mapping(source = "price", numberFormat = "$#.00")
//Date 转字符串
@Mapping(source = "currentDate", dateFormat = "dd.MM.yyyy")



@Mappings({
@Mapping(source = "birthday", target = "birthday", dateFormat = "yyyy-MM-dd HH:mm:ss"),
@Mapping(source = "name", target = "name")
})

@IterableMapping(dateFormat = "dd.MM.yyyy")
List<String> dateToString(List<Date> dates)

// 引用另一个映射器类
@Mapper(uses = PrivateDateMapper.class)
public class CartMapper {
CartDto cartToCartDto(Cart cart);
}
public class DateMapper {
public String asString(Date date) {
... ...
}
public Date toDate(String date) {
... ...
}
}

// 示例小样2 DO 转 DTO
public interface PersonDo2DtoMapping {
PersonDo2DtoMapping INSTANCE = Mappers.getMapper(PersonDo2DtoMapping.class);

@Mapping(source = "birthday", target = "birthday", dateFormat = "yyyy-MM-dd HH:mm:ss")
PersonDto do2dto(PersonDo personDo);
}

// 示例小样3 DO集合 转 DTO集合:
public interface PersonDo2DtoMapping {
PersonDo2DtoMapping INSTANCE = Mappers.getMapper(PersonDo2DtoMapping.class);

@Mapping(source = "birthday", target = "birthday", dateFormat = "yyyy-MM-dd HH:mm:ss")
PersonDto do2dto(PersonDo personDo);

// 会使用domain2dto方法进行对象list转换 
// 集合的bean的转换本质是:转换的时候调用了do2dto方法,也就是说mapsstruct能自动的使用合适的转换方法
List<PersonDto> listToList(List<PersonDo> people);
}

//示例小样4 多个类型的 DO 转换为一个类型的 DTO
public interface PersonDo2DtoMapping {
PersonDo2DtoMapping INSTANCE = Mappers.getMapper(PersonDo2DtoMapping.class);

@Mapping(source = "psersonDo.birthday", target = "birthday", dateFormat = "yyyy-MM-dd HH:mm:ss")
@Mapping(source = "userDo.name", target = "name")
PersonDto twoDoTOTwoDto(PersonDo personDo,UserDO userDo);
}

//示例小样5 自定义DO 转 DTO 的映射规则
// eg:比如的用户性别sex DO对应的是Integer类型{0:男,1:女}, DTO对应的是String类型{0:男,1:女}
/**定义PersonSexConverterRule 转换规则类*/
public class PersonSexConverterRule {
public String intToString(int sex) {
if (sex == 0) {
return "男";
} else {
return "女";
}
}
}

@Mapper(uses = PersonSexConverterRule.class)
public interface PersonDo2DtoMapping {
PersonDo2DtoMapping INSTANCE = Mappers.getMapper(PersonDo2DtoMapping.class);

// ?? 规则作用在哪个属性上,是根据属性类型进行规则匹配的?
@Mapping(source = "birthday", target = "birthday", dateFormat = "yyyy-MM-dd HH:mm:ss")
PersonDto do2dto(PersonDo personDo);
}

//示例小样6 spring整合:需要在映射接口的mapper注解中添加参数componentModel = "spring"
@Mapper(uses = PersonSexConverterRule.class,componentModel = "spring")
public interface PersonDo2DtoMapping {
PersonDo2DtoMapping INSTANCE = Mappers.getMapper(PersonDo2DtoMapping.class);

// ?? 规则作用在哪个属性上,是根据属性类型进行规则匹配的?
@Mapping(source = "birthday", target = "birthday", dateFormat = "yyyy-MM-dd HH:mm:ss")
PersonDto do2dto(PersonDo personDo);
}

因为我很懒,所以我很懒。