Maven 依赖
在 pom.xml
中加入以下依赖
1 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
| <properties> <org.mapstruct.version>1.4.2.Final</org.mapstruct.version> </properties>
<dependencies> <dependency> <groupId>org.mapstruct</groupId> <artifactId>mapstruct</artifactId> <version>${org.mapstruct.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> <path> <groupId>org.mapstruct</groupId> <artifactId>mapstruct-processor</artifactId> <version>${org.mapstruct.version}</version> </path> </annotationProcessorPaths> </configuration> </plugin> </plugins> </build>
|
如果使用了 lombok
还需要再添加以下内容
1 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
| <properties> <org.mapstruct.version>1.4.2.Final</org.mapstruct.version> <org.projectlombok.version>1.18.16</org.projectlombok.version> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> </properties>
<dependencies> <dependency> <groupId>org.mapstruct</groupId> <artifactId>mapstruct</artifactId> <version>${org.mapstruct.version}</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>${org.projectlombok.version}</version> <scope>provided</scope> </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> <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>
<path> <groupId>org.projectlombok</groupId> <artifactId>lombok-mapstruct-binding</artifactId> <version>0.1.0</version> </path> </annotationProcessorPaths> </configuration> </plugin> </plugins> </build>
|
MapStruct 是用来做两个对象之间的映射关系,在平常的开发中,我们经常会使用到 BeanUtils
这种工具。BeanUtils
的问题在于,他对每个成员亦是赋值是通过反射来做的,并且成员变量的字段名必须一致。而 MapStruct
是在编译期完成这件事情的,还可以将不同名称,不同类型的成员变量进行赋值。
定义 Mapper
在接口上打上 @Mapper
注解, MapStruct
会生成对应的接口实现。
1 2 3 4
| @Mapper public interface Convert {
}
|
使用 Convert
普通方式(推荐)
1 2 3 4 5 6
| @Mapper public interface Convert { Convert INSTANCE = Mappers.getMapper(Convert.class); @Mapping(source = "createTime", target = "createTime", dateFormat = "yyyy-MM-dd") Person personDTO2Person(PersonDTO personDTO); }
|
调用映射使用时,Convert.INSTANCE.personDTO2Person(personDTO) 即可。
Spring方式
1 2 3 4 5
| @Mapper(componentModel = "spring") public interface PersonConvert { @Mapping(source = "createTime", target = "createTime", dateFormat = "yyyy-MM-dd") Person personDTO2Person(PersonDTO personDTO); }
|
- 会检查 classpath 下是否饮食 spring 相关依赖
使用时, 用 Spring 的注解 @Autowired
注入。
@Mapping 注解
source
- 指定源名称,即要映射的对象的名称
- 不能与constant 或 expression
target
constant
- 将目标对象赋值为指定的常量
- 不能与source , defaultValue , defaultExpression 或 expression 同时存在
- 将从
String
映射到 Date
, SimpleDateFormat
进行处理。
- 将从
Number
映射到 String
, DecimalFormat
进行处理。其他类型将被忽略
expression
defaultExpression
ignore
defaultValue
- 当
source
为 null
的时候,为 target
字段指定默认值。
qualifiedBy
qualifiedByName
resultType
dependsOn
nullValueCheckStrategy
nullValuePropertyMappingStrategy
映射技巧
相同类型相同名称的映射
- 直接写对应的映射方法即可,在参数和返回值分别写对象的 Bean 对象即可。
1 2 3 4 5
| @Mapper public interface PersonConvert { PersonConvert INSTANCE = Mappers.getMapper(PersonConvert.class); Person personDTO2Person(PersonDTO personDTO); }
|
相同类型不同名称的映射
- 使用
@Mapping
注解标注对应的 source
和 target
,source
源对象的名称, target
目标名称。
1 2 3 4 5 6
| @Mapper public interface PersonConvert { PersonConvert INSTANCE = Mappers.getMapper(PersonConvert.class); @Mapping(source = "createTime", target = "time") Person personDTO2Person(PersonDTO personDTO); }
|
不同类型相同名称的映射
int 到 String
1 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
| @Data public class Person { private String name; private String age; private LocalDate createTime; }
@Data public class PersonDTO { private String name; private Integer age; private LocalDate createTime; }
@Mapper public interface PersonConvert { PersonConvert INSTANCE = Mappers.getMapper(PersonConvert.class); Person personDTO2Person(PersonDTO personDTO); }
public class PersonConvertImpl implements PersonConvert { @Override public Person personDTO2Person(PersonDTO personDTO) { if ( personDTO == null ) { return null; } Person person = new Person(); person.setName( personDTO.getName() ); if ( personDTO.getAge() != null ) { person.setAge( String.valueOf( personDTO.getAge() ) ); } person.setCreateTime( personDTO.getCreateTime() ); return person; } }
public class PersonConvertImpl implements PersonConvert { @Override public Person personDTO2Person(PersonDTO personDTO) { if ( personDTO == null ) { return null; } Person person = new Person(); person.setName( personDTO.getName() ); if ( personDTO.getAge() != null ) { person.setAge( Integer.parseInt( personDTO.getAge() ) ); } person.setCreateTime( personDTO.getCreateTime() ); return person; } }
|
enum 到 String
- 调用对应枚举的
name()
方法,然后再 set 到对应字段。
1 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
| @Data public class Person { private String name; private Integer age; private LocalDate createTime; private String sex; }
@Data public class Person { private String name; private Integer age; private LocalDate createTime; private String sex; }
@Mapper public interface PersonConvert { PersonConvert INSTANCE = Mappers.getMapper(PersonConvert.class); Person personDTO2Person(PersonDTO personDTO); }
public class PersonConvertImpl implements PersonConvert { @Override public Person personDTO2Person(PersonDTO personDTO) { if ( personDTO == null ) { return null; } Person person = new Person(); person.setName( personDTO.getName() ); if ( personDTO.getAge() != null ) { person.setAge( Integer.parseInt( personDTO.getAge() ) ); } person.setCreateTime( personDTO.getCreateTime() ); if ( personDTO.getSex() != null ) { person.setSex( personDTO.getSex().name() ); } return person; } }
|
BigDecimal 到 String
1 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
| @Data public class PersonDTO { private String name; private String age; private LocalDate createTime; private SexEnum sex; private BigDecimal power; }
@Data public class Person { private String name; private Integer age; private LocalDate createTime; private String sex; private String power; }
@Mapper public interface PersonConvert { PersonConvert INSTANCE = Mappers.getMapper(PersonConvert.class); @Mapping(target = "power", source = "power", numberFormat = "#.##E0") Person personDTO2Person(PersonDTO personDTO); }
public class PersonConvertImpl implements PersonConvert { @Override public Person personDTO2Person(PersonDTO personDTO) { if ( personDTO == null ) { return null; } Person person = new Person(); if ( personDTO.getPower() != null ) { person.setPower( createDecimalFormat( "#.##E0" ).format( personDTO.getPower() ) ); } person.setName( personDTO.getName() ); if ( personDTO.getAge() != null ) { person.setAge( Integer.parseInt( personDTO.getAge() ) ); } person.setCreateTime( personDTO.getCreateTime() ); if ( personDTO.getSex() != null ) { person.setSex( personDTO.getSex().name() ); } return person; }
private DecimalFormat createDecimalFormat( String numberFormat ) {
DecimalFormat df = new DecimalFormat( numberFormat ); df.setParseBigDecimal( true ); return df; } }
|
Date 到 String
- 支持 Date 、Calendar、LocalDate、Instant、ZonedDateTime、LocalDateTime 等多种类型
1 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
| @Data public class PersonDTO { private String name; private String age; private LocalDate createTime; private SexEnum sex; }
@Data public class Person { private String name; private Integer age; private String createTime; private String sex; }
@Mapper public interface PersonConvert {
PersonConvert INSTANCE = Mappers.getMapper(PersonConvert.class);
@Mapping(target = "createTime", source = "createTime", dateFormat = "yyyy-MM-dd HH:mm:ss") Person personDTO2Person(PersonDTO personDTO); }
public class PersonConvertImpl implements PersonConvert { @Override public Person personDTO2Person(PersonDTO personDTO) { if ( personDTO == null ) { return null; }
Person person = new Person(); if ( personDTO.getCreateTime() != null ) { person.setCreateTime( DateTimeFormatter.ofPattern( "yyyy-MM-dd HH:mm:ss" ).format( personDTO.getCreateTime() ) ); } person.setName( personDTO.getName() ); if ( personDTO.getAge() != null ) { person.setAge( Integer.parseInt( personDTO.getAge() ) ); } if ( personDTO.getSex() != null ) { person.setSex( personDTO.getSex().name() ); } return person; } }
|
不同类型不同名称的映射
- 需要在
@Mapping
注解的 target
中指定目标名称。
1 2 3 4 5 6 7
| @Mapper public interface PersonConvert { PersonConvert INSTANCE = Mappers.getMapper(PersonConvert.class);
@Mapping(source = "createTime", target = "time", dateFormat = "yyyy-MM-dd HH:mm:ss") Person personDTO2Person(PersonDTO personDTO); }
|
多个同名参数的成员变量
1 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
| @Mapper public interface PersonConvert { PersonConvert INSTANCE = Mappers.getMapper(PersonConvert.class);
@Mapping(source = "userDTO.name", target = "nickName") @Mapping(source = "personDTO.name", target = "name") Person personDTOUSer2Person(PersonDTO personDTO, UserDTO userDTO); }
public class PersonConvertImpl implements PersonConvert { @Override public Person personDTOUSer2Person(PersonDTO personDTO, UserDTO userDTO) { if ( personDTO == null && userDTO == null ) { return null; }
Person person = new Person(); if ( personDTO != null ) { person.setName( personDTO.getName() ); } if ( userDTO != null ) { person.setNickName( userDTO.getName() ); } return person; } }
|
在参数上映射而不返回值
- 上面的映射全都是把参数映射成对应的返回值,我们接收到的返回值是映射后的对象。这里的问题是,返回对象是新
new
的,如果我们已经存在一个对象,映射部分字段想要传入一个已经手动 new
好的对象来映射。
- 使用
@MappingTarget
来标记返回的对象。
1 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
| @Data public class PersonDTO { private String name; }
@Data public class UserDTO { private String name; }
@Data public class Person { private String name; private String nickName; }
@Mapper public interface PersonConvert { PersonConvert INSTANCE = Mappers.getMapper(PersonConvert.class);
@Mapping(source = "userDTO.name", target = "nickName") @Mapping(source = "personDTO.name", target = "name") void personDTOUSer2Person(PersonDTO personDTO, UserDTO userDTO, @MappingTarget Person person); }
public class PersonConvertImpl implements PersonConvert { @Override public void personDTOUSer2Person(PersonDTO personDTO, UserDTO userDTO, Person person) { if ( personDTO == null && userDTO == null ) { return; } if ( personDTO != null ) { person.setName( personDTO.getName() ); } if ( userDTO != null ) { person.setNickName( userDTO.getName() ); } } }
|
成员变量直接映射
- 即没有 get 和 set 方法,而是直接使用 public 定义的成员变量
1 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
| public class PersonDTO { public String name; public Integer age; }
public class Person { public String name; public Integer age; }
@Mapper public interface PersonConvert { PersonConvert INSTANCE = Mappers.getMapper(PersonConvert.class); Person personDTOUSer2Person(PersonDTO personDTO); }
public class PersonConvertImpl implements PersonConvert { @Override public Person personDTOUSer2Person(PersonDTO personDTO) { if ( personDTO == null ) { return null; } Person person = new Person(); person.name = personDTO.name; person.age = personDTO.age; return person; } }
|
嵌套的 Bean 映射
1 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
| @Data public class PersonDTO { private String name; private Integer age; private Record record;
@Data public static class Record { private String recordName; private Integer number; } }
@Data public class Person { private String personName; private Integer age; private Integer account; }
@Mapper public interface PersonConvert { PersonConvert INSTANCE = Mappers.getMapper(PersonConvert.class);
@Mapping(source = "record.number", target = "account") @Mapping(source = "name", target = "personName") Person personDTOUSer2Person(PersonDTO personDTO); }
public class PersonConvertImpl implements PersonConvert { @Override public Person personDTOUSer2Person(PersonDTO personDTO) { if ( personDTO == null ) { return null; } Person person = new Person(); person.setAccount( personDTORecordNumber( personDTO ) ); person.setPersonName( personDTO.getName() ); person.setAge( personDTO.getAge() ); return person; }
private Integer personDTORecordNumber(PersonDTO personDTO) { if ( personDTO == null ) { return null; } Record record = personDTO.getRecord(); if ( record == null ) { return null; } Integer number = record.getNumber(); if ( number == null ) { return null; } return number; } }
|
Builder 模式映射
1 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
| @Data public class PersonDTO { private String name; private Integer age; }
@Builder public class Person { private String name; private Integer age; }
@Mapper public interface PersonConvert { PersonConvert INSTANCE = Mappers.getMapper(PersonConvert.class); Person personDTOUSer2Person(PersonDTO personDTO); }
public class PersonConvertImpl implements PersonConvert { @Override public Person personDTOUSer2Person(PersonDTO personDTO) { if ( personDTO == null ) { return null; } PersonBuilder person = Person.builder(); person.name( personDTO.getName() ); person.age( personDTO.getAge() ); return person.build(); } }
|
构造器映射
- 只有一个公共的构造器
- 有一个
@Default
注解,可以来自任何包。
- 有一个无参构造器,同时还包含其他构造器。
- 有多个公共构造器,不含无参构造器,(编译错误)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| @Getter @AllArgsConstructor public class PersonDTO {
private String name;
private Integer age; }
@Getter @AllArgsConstructor public class Person {
private String name;
private Integer age; }
|
只有一个公共的构造器
集合映射
Map映射
其他用法
向映射器添加自定义方法
- 在接口中写一个
default
方法即可,这其实就是自己手动写个方法。**(很少会用)**
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| @Mapper public interface PersonConvert { PersonConvert INSTANCE = Mappers.getMapper(PersonConvert.class); @Mapping(source = "createTime", target = "createTime", dateFormat = "yyyy-MM-dd") Person personDTO2Person(PersonDTO personDTO);
default PersonDTO person2PersonDTO(Person person) { PersonDTO dto = new PersonDTO(); dto.setName("张三"); dto.setAge(person.getAge() + 13); dto.setCreateTime("这个时间很奇怪,我必须手动实现"); return dto; } }
|
- 在抽象类中实现。也是一样写一个
public
的方法即可,Mapstruct
的实现是,继承我们自己写的那个抽象类。(基本不用)