简介
mapstruct是一种实体类映射框架,能够通过Java注解将一个实体类的属性安全地赋值给另一个实体类。有了mapstruct,只需要定义一个映射器接口,声明需要映射的方法,在编译过程中,mapstruct会自动生成该接口的实现类,实现将源对象映射到目标对象的效果。
maven引入
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<mapstruct.version>1.5.5.Final</mapstruct.version>
<lombok.version>1.18.30</lombok.version>
</properties>
<dependencies>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
<version>${mapstruct.version}</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.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>
<!-- Lombok插件 -->
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
</path>
<!-- MapStruct插件 -->
<path>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>${mapstruct.version}</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
</plugins>
</build>
代码示例
先定义我们想要转换的实体类,假设我们想实现将User类转换为People类。People中大部分属性和User中相同,差异的部分为:性别的枚举类Sex和Gender、num和count(一个是包装类Integer,一个是int)、People中多出了一个updateTime字段
@Data
public class People {
private Long id;
private String name;
private Sex sex;
private int num;
private String desc;
private String createTime;
private LocalDateTime updateTime;
private Car car;
}
@Data
public class User {
private Long id;
private String name;
private Gender gender;
private Integer count;
private String desc;
private Car car;
private LocalDateTime createTime;
}
@Data
public class Car {
private String name;
private LocalDateTime createTime;
}
public enum Gender {
male("male", "男"),
female("female", "女"),
unknown("unknown", "未知");
}
public enum Sex {
man("man", "男"),
woman("woman", "女"),
unknown("unknown", "未知");
}
// 定义一个MapStruct接口
@Mapper(builder = @Builder(disableBuilder = true))
public interface MapStruct {
MapStruct INSTANCE = Mappers.getMapper(MapStruct.class);
/**
* 枚举类到枚举类映射
*/
@ValueMappings({
@ValueMapping(target = "man", source = "male"),
@ValueMapping(target = "woman", source = "female"),
@ValueMapping(target = "unknown", source = "unknown")
})
Sex genderToSex(Gender gender);
/**
* user实体转成people实体
*/
@Mapping(target = "num", source = "count")
@Mapping(target = "sex", source = "gender")
@Mapping(target = "id", ignore = true)
@Mapping(target = "desc", defaultValue = "特殊说明")
@Mapping(target = "updateTime", expression = "java(LocalDateTime.now())")
@Mapping(target = "createTime", dateFormat = "yyyy-MM-dd HH:mm:ss")
People toPeople(User user);
/**
* map转成people实体,map需指定类型
*/
@Mapping(target = "car", ignore = true)
People toPeople(Map<String, String> userMap);
/**
* 集合转集合
*/
List<People> toPeoples(List<User> users);
}
使用
定义好上面的mapstruct接口后,想实现实体类到实体类的转换就非常简单了
public static void main(String[] args) {
Car car = new Car();
car.setName("BYD");
car.setCreateTime(LocalDateTime.now().minusYears(5));
User user = new User();
user.setId(93002614785301L);
user.setName("Han Si");
user.setCount(15);
user.setCreateTime(LocalDateTime.now().minusMonths(36));
user.setCar(car);
user.setGender(Gender.male);
People people = MapStruct.INSTANCE.toPeople(user);
System.out.println("user to people : " + people);
// 输出 user to people : People(id=null, name=Han Si, sex=man, num=15, desc=特殊说明, createTime=2020-12-05 09:47:44, updateTime=2023-12-05T09:47:44.280, car=Car(name=BYD, createTime=2018-12-05T09:47:44.234))
Map<String, String> map = new HashMap<>();
map.put("id", "93002614785301");
map.put("name", "Han Si");
map.put("num", "15");
map.put("desc", "desc123");
map.put("createTime", "2021-10-12T19:15:30");
people = MapStruct.INSTANCE.toPeople(map);
System.out.println("map to people : " + people);
// 输出 map to people : People(id=93002614785301, name=Han Si, sex=null, num=15, desc=desc123, createTime=2021-10-12T19:15:30, updateTime=null, car=null)
}
通过@Mapping注解我们可以非常简单的定义从源字段到目标字段的转换
@Mapping 注解
- target用于标注目标实体类中的具体字段名,source表示源实体类中的具体字段名
- @Mapping(target = "id", ignore = true) :两个实体类中有相同的id属性,但是不想转到目标实体,可以使用ignore = true
- @Mapping(target = "num", source = "count") :两个实体类中有相同含义的字段,但是字段不同,可以告诉编译器目标实体类的字段名是什么
- @Mapping(target = "desc", defaultValue = "特殊说明") :defaultValue 可以定义默认值,如果给的源实体中该字段为空
- @Mapping(target = "updateTime", expression = "java(LocalDateTime.now()) ") :expression可以使用Java代码,用于生成具体的值,需要指定类的全路径或者在类上使用imports导入 eg : @Mapper(builder = @Builder(disableBuilder = true), imports = {AxxStatusEnum.class, BxxStatusEnum.class}) 。 类似的还有defaultExpression
- @Mapping(target = "createTime", dateFormat = "yyyy-MM-dd HH:mm:ss") : dateFormat可以用于时间格式化,默认使用的是SimpleDateFormat
@ValueMappings 注解
可以实现enum到enum的转换
@ValueMappings({ @ValueMapping(target = "man", source = "male"), @ValueMapping(target = "woman", source = "female"), @ValueMapping(target = "unknown", source = "unknown") }) Sex genderToSex(Gender gender);
然后使用 @Mapping(target = "sex", source = "gender")映射
@SubclassMapping
@SubclassMapping( source = BananaDto.class, target = Banana.class )
Fruit map( FruitDto source );
其实定义好上面的mapstruct接口后,编译过后,在target/classes下会自动生成如下代码,这种自动编译的,会比使用反射实现的实体类转换工具类性能更好。
// 编译器自动生成
@Generated(
value = "org.mapstruct.ap.MappingProcessor",
date = "2023-11-10T15:23:35+0800",
comments = "version: 1.5.5.Final, compiler: javac, environment: Java 1.8.0_371 (Oracle Corporation)"
)
public class MapStructImpl implements MapStruct {
private final DateTimeFormatter dateTimeFormatter_yyyy_MM_dd_HH_mm_ss_11333195168 = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
@Override
public Sex genderToSex(Gender gender) {
if (gender == null) {
return null;
}
Sex sex;
switch (gender) {
case male:
sex = Sex.man;
break;
case female:
sex = Sex.woman;
break;
case unknown:
sex = Sex.unknown;
break;
default:
throw new IllegalArgumentException("Unexpected enum constant: " + gender);
}
return sex;
}
@Override
public People toPeople(User user) {
if (user == null) {
return null;
}
People people = new People();
if (user.getCount() != null) {
people.setNum(user.getCount());
}
people.setSex(genderToSex(user.getGender()));
if (user.getDesc() != null) {
people.setDesc(user.getDesc());
} else {
people.setDesc("特殊说明");
}
if (user.getCreateTime() != null) {
people.setCreateTime(dateTimeFormatter_yyyy_MM_dd_HH_mm_ss_11333195168.format(user.getCreateTime()));
}
people.setName(user.getName());
people.setCar(user.getCar());
people.setUpdateTime(LocalDateTime.now());
return people;
}
@Override
public People toPeople(Map<String, String> userMap) {
if (userMap == null) {
return null;
}
People people = new People();
if (userMap.containsKey("id")) {
people.setId(Long.parseLong(userMap.get("id")));
}
if (userMap.containsKey("name")) {
people.setName(userMap.get("name"));
}
if (userMap.containsKey("sex")) {
people.setSex(Enum.valueOf(Sex.class, userMap.get("sex")));
}
if (userMap.containsKey("num")) {
people.setNum(Integer.parseInt(userMap.get("num")));
}
if (userMap.containsKey("desc")) {
people.setDesc(userMap.get("desc"));
}
if (userMap.containsKey("createTime")) {
people.setCreateTime(userMap.get("createTime"));
}
if (userMap.containsKey("updateTime")) {
people.setUpdateTime(LocalDateTime.parse(userMap.get("updateTime")));
}
return people;
}
@Override
public List<People> toPeoples(List<User> users) {
if (users == null) {
return null;
}
List<People> list = new ArrayList<People>(users.size());
for (User user : users) {
list.add(toPeople(user));
}
return list;
}
}