- 映射器定义
- 基本映射
- 自定义映射方法
- 从多个源对象映射
- 映射嵌套对象
- 更新现有实例
- 继承配置
- 逆映射
- 映射期间的异常处理
- 数据类型转换
- 隐式类型转换
- 映射集合
- 映射策略
- 映射流
- 映射枚举
- 定义默认值或常量
- 定义默认表达式
- 映射器检索策略
- 映射定制
- 装饰器
- @BeforeMapping和@AfterMapping
- 参考文献
注:
- 没有提供对应的对象,自己实现,提高认识
- 学习方式:最好是对class进行一个反编译,看看他生成的代码。如果发现一些类型没设置成功也可以通过反编译查看。
- 反编译工具:java-decompiler
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
<version>${org.mapstruct.version}</version>
</dependency>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>${org.mapstruct.version}</version>
</dependency>
映射器定义
@Test
void test() {
BasicMapper instance = BasicMapper.INSTANCE;
BasicUserDTO convert = instance.convert(user);
}
基本映射
- 如果两个字段名称相同会自动映射。
- 如果两个字段名称不相同则需要使用
@Mapping
进行映射,参考 - 从多个源对象映射
@Mapper
public interface BasicMapper {
BasicMapper INSTANCE = Mappers.getMapper(BasicMapper.class);
BasicUserDTO convert(BasicUser user);
}
自定义映射方法
@Mapper
public interface BasicMapper {
BasicMapper INSTANCE = Mappers.getMapper(BasicMapper.class);
BasicUserDTO convert(BasicUser user);
default PersonDTO convertCustom(BasicUser user) {
return PersonDTO
.builder()
.id(String.valueOf(user.getId()))
.firstName(user.getName().substring(0, user.getName().indexOf(" ")))
.lastName(user.getName().substring(user.getName().indexOf(" ") + 1))
.build();
}
}
@Mapper
public abstract class BasicMapper {
public abstract BasicUserDTO convert(BasicUser user);
public PersonDTO convertCustom(BasicUser user) {
return PersonDTO
.builder()
.id(String.valueOf(user.getId()))
.firstName(user.getName().substring(0, user.getName().indexOf(" ")))
.lastName(user.getName().substring(user.getName().indexOf(" ") + 1))
.build();
}
}
从多个源对象映射
- 当入参和返回的参数不匹配时,或者 有多个入参对象时,可以通过该方式指定需要映射到哪个字段
- source:传入进来的参数
- target:返回的参数
@Mapping(source = "user.id", target = "id")
@Mapping(source = "user.name", target = "firstName")
@Mapping(source = "education.degreeName", target = "educationalQualification")
@Mapping(source = "address.city", target = "residentialCity")
@Mapping(source = "address.country", target = "residentialCountry")
PersonDTO convert(BasicUser user, Education education, Address address);
映射嵌套对象
-
当有嵌套对象时,可以采用@Mapper的uses指定一个嵌套对象对应的映射类
@Data
@Builder
@ToString
public class BasicUser {
private int id;
private String name;
private List<Manager> managerList;
}
@Mapper(uses = {ManagerMapper.class})
public interface UserMapper {
UserMapper INSTANCE = Mappers.getMapper(UserMapper.class);
@Mapping(source = "user.id", target = "id")
@Mapping(source = "user.name", target = "firstName")
@Mapping(source = "education.degreeName", target = "educationalQualification")
@Mapping(source = "address.city", target = "residentialCity")
@Mapping(source = "address.country", target = "residentialCountry")
PersonDTO convert(BasicUser user, Education education, Address address);
}
-
在生成方法时,会通过指定的嵌套类对这个嵌套对象进行映射,如下所示
更新现有实例
- 使用映射更新现有的 DTO
- 对需要更新的映射添加一个
@MappingTarget
注解,就会对其进行更新
@Mapping(source = "address.city", target = "residentialCity")
@Mapping(source = "address.country", target = "residentialCountry")
void updateExisting(Address address, @MappingTarget PersonDTO personDTO);
PersonDTO personDTO = UserMapper.INSTANCE.convert(address);
UserMapper.INSTANCE.updateExisting(address,personDTO);
继承配置
- 对于重复的配置,使用
@InheritConfiguration
,MapStruct 会查找已配置的方法,并且进行应用
@Mapper
public interface ManagerMapper {
ManagerMapper INSTANCE = Mappers.getMapper(ManagerMapper.class);
@Mapping(source = "address.city", target = "residentialCountry")
@Mapping(source = "address.country", target = "residentialCity")
ManagerDTO convert(Manager manager);
@InheritConfiguration
void updateExisting(Manager manager, @MappingTarget ManagerDTO managerDTO);
}
逆映射
-
想定义一个双向映射
如:
-
Entity 映射 DTO
-
DTO 映射 Entity
-
使用@InheritInverseConfiguration
会自动的反转配置
@Mapper
public interface UserMapper {
UserMapper INSTANCE = Mappers.getMapper(UserMapper.class);
BasicUserDTO convert(BasicUser user);
@InheritInverseConfiguration
BasicUser convert(BasicUserDTO userDTO);
}
映射期间的异常处理
数据类型转换
隐式类型转换
-
数值
@Mapping(source = "employment.salary",
target = "salary",
numberFormat = "$#.00")
PersonDTO convert(BasicUser user,
Education education,
Address address,
Employment employment);
personDTO.setSalary( new DecimalFormat( "$#.00" ).format(
employment.getSalary() ) );
-
日期
@Mapping(source = "dateOfBirth",
target = "dateOfBirth",
dateFormat = "dd/MMM/yyyy")
ManagerDTO convert(Manager manager);
managerDTO.setDateOfBirth(
new SimpleDateFormat( "dd/MMM/yyyy" )
.parse( manager.getDateOfBirth() ) );
managerDTO.setDateOfBirth( new SimpleDateFormat().parse(
manager.getDateOfBirth() ) );
映射集合
-
通过循环遍历,进行映射。
-
如果使用了@Mapping的uses则会自动调用此对应的映射方法来执行元素转换。
-
简单使用
@Mapper
public interface CollectionMapper {
CollectionMapper INSTANCE = Mappers.getMapper(CollectionMapper.class);
Set<String> convert(Set<Long> ids);
Set<EmploymentDTO> convertEmployment(Set<Employment> employmentSet);
}
-
需要对实体进行自定义映射,则需要先定义实体之间的转换方法。
@Mapper
public interface CollectionMapper {
CollectionMapper INSTANCE = Mappers.getMapper(CollectionMapper.class);
@Mapping(source = "degreeName", target = "degree")
@Mapping(source = "institute", target = "college")
@Mapping(source = "yearOfPassing", target = "passingYear")
EducationDTO convert(Education education);
List<EducationDTO> convert(List<Education> educationList);
}
educationDTO.degree( education.getDegreeName() );
educationDTO.college( education.getInstitute() );
educationDTO.passingYear( education.getYearOfPassing() );
-
对map进行映射
- 可以通过 keyNumberFormat 和 valueDateFormat 对转入的键值做一个转换
@Mapper
public interface CollectionMapper {
CollectionMapper INSTANCE = Mappers.getMapper(CollectionMapper.class);
@MapMapping(keyNumberFormat = "#L", valueDateFormat = "dd.MM.yyyy")
Map<String, String> map(Map<Long, Date> dateMap);
}
String key = new DecimalFormat( "#L" ).format( entry.getKey() );
String value = new SimpleDateFormat( "dd.MM.yyyy" ).format( entry.getValue() );
映射策略
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-s4eI45Pl-1660918092350)(images/mapstruct - 集合映射策略.png)]
@Mapper(collectionMappingStrategy = CollectionMappingStrategy.ADDER_PREFERRED)
public interface PersonMapperAdderPreferred {
PersonDTO map(Person person);
}
映射流
- 和映射集合相同,只是
Stream
会从提供的返回Iterable
:
@Mapper
public interface CollectionMapper {
CollectionMapper INSTANCE = Mappers.getMapper(CollectionMapper.class);
Set<String> convertStream(Stream<Long> ids);
@Mapping(source = "degreeName", target = "degree")
@Mapping(source = "institute", target = "college")
@Mapping(source = "yearOfPassing", target = "passingYear")
EducationDTO convert(Education education);
List<EducationDTO> convert(Stream<Education> educationStream);
}
return ids.map( long1 -> String.valueOf( long1 ) )
.collect( Collectors.toCollection( HashSet<String>::new ) );
映射枚举
-
名字相同,则直接映射即可
-
名字不相同,使用@ValueMapping
进行映射
无法识别源值,抛出 IllegalStateException。
public enum DesignationCode {CEO}
public enum DesignationConstant {CHIEF_EXECUTIVE_OFFICER}
@Mapper
public interface UserMapper {
UserMapper INSTANCE = Mappers.getMapper(UserMapper.class);
@ValueMappings({
@ValueMapping(source = "CEO", target = "CHIEF_EXECUTIVE_OFFICER"),
})
DesignationConstant convertDesignation(DesignationCode code);
}
-
如果有前缀,则使用如下4个属性进行映射
suffix
- 在源枚举上应用后缀stripSuffix
- 从源枚举中去除后缀prefix
- 在源枚举上应用前缀stripPrefix
- 从源枚举中去除前缀
public enum DegreeStream {MATHS}
public enum DegreeStreamPrefix {MSC_MATHS}
@Mapper
public interface UserMapper {
UserMapper INSTANCE = Mappers.getMapper(UserMapper.class);
@EnumMapping(nameTransformationStrategy = "prefix", configuration = "MSC_")
DegreeStreamPrefix convert(DegreeStream degreeStream);
@EnumMapping(nameTransformationStrategy = "stripPrefix", configuration = "MSC_")
DegreeStream convert(DegreeStreamPrefix degreeStreamPrefix);
}
定义默认值或常量
- defaultValue:当值不存在时,则使用默认值
- constant:映射到目标枚举类型中具有相同名称的常量
@Mapper(collectionMappingStrategy = CollectionMappingStrategy.ADDER_PREFERRED,
uses = {CollectionMapper.class, ManagerMapper.class, Validator.class},
imports = UUID.class )
public interface UserMapper {
UserMapper INSTANCE = Mappers.getMapper(UserMapper.class);
@Mapping(source = "education.yearOfPassing", target = "education.passingYear",
defaultValue = "2001")
@Mapping(source = "employment", target = ".")
@Mapping(target = "residentialCountry", constant = "US")
PersonDTO convert(BasicUser user,
Education education,
Address address,
Employment employment);
}
定义默认表达式
- 用于使用java表达式
- 在源属性为
null
时使用,才会触发。 - 还需要导入对应的类
@Mapper( imports = UUID.class )
public interface UserMapper {
UserMapper INSTANCE = Mappers.getMapper(UserMapper.class);
@Mapping(source = "user.id", target = "id",
defaultExpression = "java( UUID.randomUUID().toString() )")
PersonDTO convert(BasicUser user,
Education education,
Address address,
Employment employment);
}
映射器检索策略
-
不使用依赖注入框架,使用Mappers
获取映射器实例
@Mapper
public interface UserMapper {
UserMapper INSTANCE = Mappers.getMapper(UserMapper.class);
}
PersonDTO personDTO = UserMapper.INSTANCE.convert(user);
-
使用@componentModel
注解导入依赖注入
@Mapper(componentModel = "spring")
public interface UserMapper {}
@Component
public class UserMapperImpl implements UserMapper {}
@Controller
public class UserController() {
@Autowired
private UserMapper userMapper;
}
映射定制
- 使用装饰器模式,进行自定义
- 使用
@BeforeMapping
/@AfterMapping
,进行通用的设置
装饰器
- 定义一个Decorator类,在使用
@DecoratedWith
使其生效 - 对需要自定义映射的方法进行实现,其他的方法用默认实现生成对原始映射器的委托。
public abstract class UserMapperDecorator implements UserMapper {
private final UserMapper delegate;
protected UserMapperDecorator (UserMapper delegate) {
this.delegate = delegate;
}
@Override
public PersonDTO convert(BasicUser user,
Education education,
Address address,
Employment employment) {
PersonDTO dto = delegate.convert(user, education, address, employment);
if (user.getName().split("\\w+").length > 1) {
dto.setFirstName(user.getName().substring(0, user.getName().lastIndexOf(' ')));
dto.setLastName(user.getName().substring(user.getName().lastIndexOf(" ") + 1));
}
else {
dto.setFirstName(user.getName());
}
return dto;
}
}
@Mapper
@DecoratedWith(UserMapperDecorator.class)
public interface UserMapper {
UserMapper INSTANCE = Mappers.getMapper(UserMapper.class);
PersonDTO convert(BasicUser user, Education education, Address address, Employment employment);
}
@BeforeMapping和@AfterMapping
@BeforeMapping
用于执行前,运行指定的逻辑@AfterMapping
用于执行后,运行指定的逻辑
@Mapper
@DecoratedWith(UserMapperDecorator.class)
public interface UserMapper {
UserMapper INSTANCE = Mappers.getMapper(UserMapper.class);
@BeforeMapping
default void validateMangers(BasicUser user) {
if (Objects.isNull(user.getManagerList())) {
user.setManagerList(new ArrayList<>());
}
}
@Mapping(target = "residentialCountry", constant = "US")
void updateExisting( Address address );
@AfterMapping
default void updateResult(BasicUser user,
@MappingTarget PersonDTO personDTO) {
personDTO.setFirstName(personDTO.getFirstName().toUpperCase());
personDTO.setLastName(personDTO.getLastName().toUpperCase());
}
}
参考文献
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)