MapStruct是一个Java注释处理器,用于生成类型安全的bean映射类。
您所要做的就是定义一个映射器接口,该接口声明任何所需的映射方法。在编译期间,MapStruct将生成此接口的实现。此实现使用纯Java方法调用来映射源对象和目标对象,即无反射或类似内容。
与手工编写映射代码相比,MapStruct通过生成繁琐且容易出错的代码来节省时间。MapStruct遵循一种约定而非配置的方法,它使用合理的默认值,但允许配置或实现特殊行为。
与动态映射框架相比,MapStruct具有以下优势:
- 通过使用普通方法调用(settter/getter)而不是反射来快速执行
- 编译时类型安全性:只能映射相互映射的对象和属性,不能将order实体意外映射到customer DTO等。
- 如果有如下问题,编译时会抛出异常
- 映射不完整(并非所有目标属性都被映射)
- 映射不正确(找不到正确的映射方法或类型转换)
工程配置,引入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;
}
}