# Spring Boot 集成 MapStruct
# MapStruct
MapStruct
是一个代码生成器,它基于约定优先于配置的方法,极大地简化了javabean
类型之间映射的实现。
多层应用程序通常需要在不同的对象模型(例如实体和DTO
)之间进行映射,编写这样的映射代码是一项乏味且容易出错的任务,MapStruct
旨在通过尽可能自动化来简化这项工作。
与其他映射框架相比,MapStruct
在编译时生成bean映射,且生成的映射代码使用纯方法调用,因此速度快、类型安全且易于理解,所以确保了高性能,允许快速开发人员反馈和彻底的错误检查。(BeanUtils
的copyProperties
的方法利用了反射,有性能损耗)
# mapstruct-lombok
mapstruct
结合lombok
使用需要在plugin maven-compiler-plugin
下增加配置来解决mapstruct
和lombok
插件的冲突。
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright MapStruct Authors.
Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.mapstruct.examples.lombok</groupId>
<artifactId>mapstruct-examples-lombok</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<org.mapstruct.version>1.4.1.Final</org.mapstruct.version>
<org.projectlombok.version>1.18.12</org.projectlombok.version>
</properties>
<dependencies>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
<version>${org.mapstruct.version}</version>
</dependency>
<!-- lombok dependencies should not end up on classpath -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${org.projectlombok.version}</version>
<scope>provided</scope>
</dependency>
<!-- IntelliJ pre 2018.1.1 requires the mapstruct processor to be present as provided dependency -->
<!-- <dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>${org.mapstruct.version}</version>
<scope>provided</scope>
</dependency>-->
</dependencies>
<build>
<pluginManagement>
<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>
<!-- See https://maven.apache.org/plugins/maven-compiler-plugin/compile-mojo.html -->
<!-- Classpath elements to supply as annotation processor path. If specified, the compiler -->
<!-- will detect annotation processors only in those classpath elements. If omitted, the -->
<!-- default classpath is used to detect annotation processors. The detection itself depends -->
<!-- on the configuration of annotationProcessors. -->
<!-- -->
<!-- According to this documentation, the provided dependency processor is not considered! -->
<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>
</annotationProcessorPaths>
</configuration>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
特别注意
mapstruct
的版本为1.4.1.Final
,lombok
的版本1.18.12
,版本不对应会导致编译失败。
# mapstruct-field-mapping
- Customer.java
public class Customer {
private Long id;
private String name;
private Collection<OrderItem> orderItems;
}
2
3
4
5
6
- CustomerDto.java
public class CustomerDto {
public Long id;
public String customerName;
public List<OrderItemDto> orders;
}
2
3
4
5
6
- OrderItem.java
public class OrderItem {
private String name;
private Long quantity;
}
2
3
4
5
- OrderItemDto.java
public class OrderItemDto {
public String name;
public Long quantity;
}
2
3
4
5
- OrderItemMapper.java
@Mapper
public interface OrderItemMapper {
OrderItemMapper MAPPER = Mappers.getMapper(OrderItemMapper.class);
OrderItem toOrder(OrderItemDto orderItemDto);
@InheritInverseConfiguration
OrderItemDto fromOrder(OrderItem orderItem);
}
2
3
4
5
6
7
8
9
10
- CustomerMapper.java
@Mapper(uses = { OrderItemMapper.class })
public interface CustomerMapper {
CustomerMapper MAPPER = Mappers.getMapper( CustomerMapper.class );
@Mapping(source = "orders", target = "orderItems")
@Mapping(source = "customerName", target = "name")
Customer toCustomer(CustomerDto customerDto);
@InheritInverseConfiguration
CustomerDto fromCustomer(Customer customer);
}
2
3
4
5
6
7
8
9
10
11
12
# Mapstruct 中使用 lombok @Builder 丢失父类属性问题
(推荐)项目中使用MybatisPlus
的情况下,所有的Entity
的父类代码如下(其中Model
是一个开源的MybatisPlus
,数据库Entity需要继承该Model
才可以使用基础的CRUD
):
/**
* The class Base entity.
*
* @author : quansheng.zhang
* @date : 2019/2/28 21:00
*/
@Data
@EqualsAndHashCode(callSuper = false)
public class BaseEntity<T extends Model<?>> extends Model<T> {
private static final long serialVersionUID = -2237290464565384433L;
/**
* 主键ID
*/
@TableId(type = IdType.AUTO)
private Integer id;
/**
* 创建人
*/
@TableField(fill = FieldFill.INSERT)
private Integer createdUser;
/**
* 记录创建时间,默认当前时间
*/
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@TableField(fill = FieldFill.INSERT)
private LocalDateTime createdTime;
/**
* 更新人
*/
@TableField(fill = FieldFill.INSERT)
private Integer updatedUser;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@TableField(fill = FieldFill.INSERT_UPDATE)
private LocalDateTime updatedTime;
/**
* 是否删除
*/
@TableField(value = "deleted", fill = FieldFill.INSERT)
private boolean deleted;
@Override
protected Serializable pkVal() {
return this.id;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
我们知道,lombok 的 @Builder
注解无法对父类属性进行赋值(@SuperBuilder还是实验阶段),所以Mapstruct
无法对Entity
父类的属性赋值。
可以使用builder = @Builder(disableBuilder = true)
来关闭mapstruct
使用builder
解决此问题。
@Mapper(componentModel = "spring", builder = @Builder(disableBuilder = true))
public interface EntityAssembler {
}
2
3
4
# Mapstruct 中使用 MybatisPlus 通用枚举赋值
枚举属性,实现 IEnum 接口如下:
public enum AgeEnum implements IEnum<Integer> {
ONE(1, "一岁"),
TWO(2, "二岁"),
THREE(3, "三岁");
private int value;
private String desc;
@Override
public Integer getValue() {
return this.value;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
enum
转换成 int
@Mappings({
@Mapping(target = "age", source = "age.value")
})
2
3
int
转换成 enum
使用Expressions (opens new window) 实现:
@Mappings({
@Mapping(target = "type", expression = "java(com.zhangmen.game.domain.enums.CourseCswareTypeEnum.getByValue(courseCswareCommand.getType()))"),
})
2
3
# Mapstruct 中使用 BigDecimal Date LocalDateTime
数据类型转换 Implicit type conversions (opens new window)
Example 31. Conversion from int to String
@Mapper
public interface CarMapper {
@Mapping(source = "price", numberFormat = "$#.00")
CarDto carToCarDto(Car car);
@IterableMapping(numberFormat = "$#.00")
List<String> prices(List<Integer> prices);
}
2
3
4
5
6
7
8
9
Example 33. Conversion from Date to String
@Mapper
public interface CarMapper {
@Mapping(source = "manufacturingDate", dateFormat = "dd.MM.yyyy")
CarDto carToCarDto(Car car);
@IterableMapping(dateFormat = "dd.MM.yyyy")
List<String> stringListToDateList(List<Date> dates);
}
2
3
4
5
6
7
8
9
参考文档