摘要:
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
如何使用
该例子包含了:
- 隐式转换(字段名相同会直接转换,如果类型不同,会根据内置的类型转换规则进行转换,见:mapstruct.org/documentati…)
- 多个源参数映射
- 表达式自定义映射
- 集合映射
- 逆向映射
该例子未包含:
- Map to Bean的映射,mapstruct 1.5以前实现不方便,将会在1.5简化实现该功能
- 装饰器自定义映射
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);