<dependency>
<groupId>org.mapstructgroupId>
<artifactId>mapstructartifactId>
<version>${org.mapstruct.version}version>
dependency>
<dependency>
<groupId>org.mapstructgroupId>
<artifactId>mapstruct-processorartifactId>
<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();
}
}
@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);
}
在生成方法时,会通过指定的嵌套类对这个嵌套对象进行映射,如下所示
// 生成了一个managerListToManagerDTOList对嵌套类进行解析
@MappingTarget注解,就会对其进行更新@Mapping(source = "address.city", target = "residentialCity")
@Mapping(source = "address.country", target = "residentialCountry")
void updateExisting(Address address, @MappingTarget PersonDTO personDTO);
// 生成了一个DTO
PersonDTO personDTO = UserMapper.INSTANCE.convert(address);
// 对这个personDTO进行了更新
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);
// mapping的配置效果,也会被下面的反转映射所使用
BasicUserDTO convert(BasicUser user);
@InheritInverseConfiguration // 反转映射
BasicUser convert(BasicUserDTO userDTO);
}
自定义校检规则,映射期间如果发现跟校检的要求不匹配,则抛出异常(自定义)
步骤:
public class ValidationException extends RuntimeException {
public ValidationException(String message, Throwable cause) {
super(message, cause);
}
public ValidationException(String message) {
super(message);
}
}
自定义校检规则
方法名要求:validate字段名(字段类型)
注意事项:校检的字段类型要和形参以及异常进行匹配,否则的话匹配不到则不会生效
public class Validator {
public int validateId(int id) throws ValidationException {
if(id < 0){
throw new ValidationException("Invalid ID value");
}
return id;
}
}
使用
// 导入校检规则
@Mapper(uses = { Validator.class})
public interface UserMapper {
UserMapper INSTANCE = Mappers.getMapper(UserMapper.class);
// 抛出对应的异常
BasicUserDTO convert(BasicUser user) throws ValidationException;
}
数值
@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进行映射
@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() );
ACCESSOR_ONLY[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-s4eI45Pl-1660918092350)(images/mapstruct - 集合映射策略.png)]
// 使用ADDER_PREFERRED策略
@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);
}
@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);
}
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 ,进行通用的设置@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用于执行后,运行指定的逻辑@Mapper
@DecoratedWith(UserMapperDecorator.class)
public interface UserMapper {
UserMapper INSTANCE = Mappers.getMapper(UserMapper.class);
// 执行前, 如果manager为null则设置一个空集合
@BeforeMapping
default void validateMangers(BasicUser user) {
if (Objects.isNull(user.getManagerList())) {
user.setManagerList(new ArrayList<>());
}
}
@Mapping(target = "residentialCountry", constant = "US")
void updateExisting( Address address );
// 执行后,对firstName和LastName进行一个字符转换
@AfterMapping
default void updateResult(BasicUser user,
@MappingTarget PersonDTO personDTO) {
personDTO.setFirstName(personDTO.getFirstName().toUpperCase());
personDTO.setLastName(personDTO.getLastName().toUpperCase());
}
}