Java属性值拷贝工具-MapStruct,替代BeanUtil利器

97 阅读3分钟

摘要:

java中的属性值拷贝,替代BeanUti和BeanCopy,更灵活、更安全的属性值拷贝方式。

前提

使用Jdk8,IDEA工具,lombok

如何配置

1.pom.xml

<properties>
        <mapstruct.version>1.4.2.Final</mapstruct.version>
        <lombok.version>1.18.10</lombok.version>
        <lombok-mapstruct-binding.version>0.2.0</lombok-mapstruct-binding.version>
 
    </properties>
 
<dependency>
            <groupId>org.mapstruct</groupId>
            <artifactId>mapstruct</artifactId>
            <version>${mapstruct.version}</version>
        </dependency>
        <dependency>
            <groupId>org.mapstruct</groupId>
            <artifactId>mapstruct-processor</artifactId>
            <version>${mapstruct.version}</version>
            <scope>provided</scope>
        </dependency>
 
<plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-compiler-plugin</artifactId>
                    <version>3.8.0</version>
                    <configuration>
                        <source>1.8</source>
                        <target>1.8</target>
                        <annotationProcessorPaths>
                            <path>
                                <groupId>org.mapstruct</groupId>
                                <artifactId>mapstruct-processor</artifactId>
                                <version>${mapstruct.version}</version>
                            </path>
                            <path>
                                <groupId>org.projectlombok</groupId>
                                <artifactId>lombok</artifactId>
                                <version>${lombok.version}</version>
                            </path>
                            <path>
                                <groupId>org.projectlombok</groupId>
                                <artifactId>lombok-mapstruct-binding</artifactId>
                                <version>${lombok-mapstruct-binding.version}</version>
                            </path>
                        </annotationProcessorPaths>
                    </configuration>
                </plugin>

2.IDEA plugin

MapStruct Support

如何使用

该例子包含了:

  1. 隐式转换(字段名相同会直接转换,如果类型不同,会根据内置的类型转换规则进行转换,见:mapstruct.org/documentati…
  2. 多个源参数映射
  3. 表达式自定义映射
  4. 集合映射
  5. 逆向映射

该例子未包含:

  1. Map to Bean的映射,mapstruct 1.5以前实现不方便,将会在1.5简化实现该功能
  2. 装饰器自定义映射

1.转换的数据类

@Data
@Builder
public class UserBO {
    private Long id;
    private String name;
    private Date birthDate;
    private Integer height;
    private BigDecimal salary;
    private GenderEnum gender;
    private CompanyBO companyBO;
}
 
enum GenderEnum {
    male,
    female;
}
 
@Data
@Builder
public class CompanyBO {
    private Long id;
    private String name;
    private String taxNum;
    private String cityCode;
}
 
@Data
public class UserCompanyVO {
    private Long userId;
    private String userName;
    private Long companyId;
    private String companyName;
    private String height;
    private String birthDate;
    private String gender;
    private String salary;
    private String cityName;
}

2.转换类,IDEA安装插件后,可查看该类编译后的源码

@Mapper
public interface UserConverter {
    UserConverter INSTANCE = Mappers.getMapper(UserConverter.class);
 
    /**
     *  UserBO --> UserCompanyVO
     */
    @Mappings({
            @Mapping(source = "id", target = "userId"),
            @Mapping(source = "name", target = "userName"),
            @Mapping(source = "salary", target = "salary", numberFormat = "#.00"),
            @Mapping(source = "birthDate", target = "birthDate", dateFormat = DatePattern.NORM_DATETIME_PATTERN),
            @Mapping(source = "companyBO.name", target = "companyName")
    })
    UserCompanyVO toVO(UserBO userBO);
 
    /**
     *  UserBO + CompanyBO --> UserCompanyVO
     */
    @Mappings({
            @Mapping(source = "userBO.id", target = "userId"),
            @Mapping(source = "userBO.name", target = "userName"),
            @Mapping(source = "userBO.salary", target = "salary", numberFormat = "#.00"),
            @Mapping(source = "companyBO.id", target = "companyId"),
            @Mapping(source = "companyBO.name", target = "companyName"),
            @Mapping(target = "cityName", expression = "java(com.frank.umlproject.mapstruct.MapStructTest.cityNameByCode(companyBO.getCityCode()))")
 
    })
    UserCompanyVO toVo1(UserBO userBO, CompanyBO companyBO);
 
    void updateByVO(UserCompanyVO userCompanyVO, @MappingTarget UserBO userBO);
 
    /**
     *  UserCompanyVO --> UserBO (逆向映射使用 toVO configuration)
     */
    @InheritInverseConfiguration(name = "toVO")
    UserBO toBO(UserCompanyVO userCompanyVO);
 
    /**
     *  集合映射
     */
    List<UserCompanyVO> toVOList(List<UserBO> userBOList);
 
    /**
     *  userBO.height --> userCompanyBO.height
     */
    default String getHeight(Integer height) {
        if (Objects.isNull(height)) {
            return null;
        }
        if (height > 170) {
            return "tall";
        } else {
            return "short";
        }
    }
}

3.测试类

public class MapStructTest {
       private static void test3() {
        UserCompanyVO userCompanyVO = new UserCompanyVO();
        userCompanyVO.setUserId(111L);
        userCompanyVO.setUserName("frank");
        userCompanyVO.setGender("male");
        userCompanyVO.setSalary("110");
        userCompanyVO.setBirthDate("2021-02-02 00:00:00");
        userCompanyVO.setCompanyName("发票公司");
        UserBO userBO = UserConverter.INSTANCE.toBO(userCompanyVO);
        System.out.println(JSONUtil.toJsonStr(userBO));
    }
 
    private static void test2() {
        UserBO userBO = UserBO.builder().birthDate(new Date()).id(111L).name("abc").gender(GenderEnum.male)
                .salary(BigDecimal.TEN).build();
        CompanyBO companyBO = CompanyBO.builder().id(222L).name("发票公司").cityCode("bj").build();
        UserCompanyVO userCompanyVO = UserConverter.INSTANCE.toVo1(userBO, companyBO);
        System.out.println(JSONUtil.toJsonStr(userCompanyVO));
    }
 
    private static void test1() {
        UserBO userBO = UserBO.builder().birthDate(new Date()).id(111L).name("abc").gender(GenderEnum.male)
                .salary(BigDecimal.TEN).height(185).build();
        UserCompanyVO userCompanyVO = UserConverter.INSTANCE.toVO(userBO);
        System.out.println(JSONUtil.toJsonStr(userCompanyVO));
    }
 
 
    public static String cityNameByCode(String code) {
        if (code.equals("bj")) {
            return "北京";
        }
        return null;
    }
}

补充

1.MapStruct 注解的关键词

@Mapper 只有在接口加上这个注解, MapStruct 才会去实现该接口
    @Mapper 里有个 componentModel 属性,主要是指定实现类的类型,一般用到两个
        default:默认,可以通过 Mappers.getMapper(Class) 方式获取实例对象
        spring:在接口的实现类上自动添加注解 @Component,可通过 @Autowired 方式注入
@Mapping:属性映射,若源对象属性与目标对象名字一致,会自动映射对应属性
    source:源属性
    target:目标属性
    dateFormat:String 到 Date 之间相互转换,通过 SimpleDateFormat,该值为 SimpleDateFormat的日期格式
    numberFormat:String 到 Number 之间的相互转换,通过DecimalFormat,如:#.00
    expression:通过表达式调用java方法,如:expression = "java(com.frank.umlproject.mapstruct.MapStructTest.cityNameByCode(companyBO.getCityCode()))"
    ignore: 忽略这个字段
@Mappings:配置多个@Mapping
@MappingTarget 用于更新已有对象
@InheritConfiguration 用于继承配置
@InheritInverseConfiguration 反向继承

2.spring注入的方式使用

@Mapper(componentModel="spring")
public interface PersonConverter {
    PersonDTO domain2dto(Person person);
}
 
@Autowired
private PersonConverter personConverter;
 
personConverter.domain2dto(person);