Mapstruc是一款高性能的对象转换工具,就性能而言比Beanutils.copy()要快很多。它对于对象转换的处理是在项目编译的时候基于注解配置生成对应的对象转换代码,并且没有各种繁杂的校验逻辑,运行的时候不需要再通过反射获取对象信息。
配置:
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
<version>1.4.2.Final</version>
</dependency>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>1.8</source> <!-- depending on your project -->
<target>1.8</target> <!-- depending on your project -->
<annotationProcessorPaths>
<path>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>${org.mapstruct.version}</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
</plugins>
</build>
实体对象:
public class UserDto {
private String name;
private Integer age;
private Date createTime;
}
public class UserPo {
private String name;
private Integer age;
private Date createTime;
private String city;
private String mycountry;
private Status status;
public enum Status{
free,
busy;
}
}
public class AddressDTO {
private String country;
private String province;
private String city;
}
public class UserDto1 {
private String nickname;
private Integer age;
// 类型和UserDto不同 ->日期类型可以自动转换
private String createTime;
}
定义转换器:注解来自org.mapstruct.*,不用导成mybatis的包!!!
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.factory.Mappers;
/**
* componentModel:设置使用模式
* spring:可以通过注解注入到spring容器中
*/
@Mapper(componentModel = "spring")
public interface SpringUserMapper{
// 会自动把两个源对象中的属性复制到目标对象
@Mapping(source = "userDTO.name",target = "name")
//将AddressDTO的部分属性赋值到UserPo
@Mapping(source = "addressDTO.city",target = "city")
@Mapping(source = "addressDTO.country",target = "mycountry")
UserPo to(UserDto userDTO, AddressDTO addressDTO);
}
源对象和目标对象属性名称一致时自动映射,不一致时通过@mapping指定属性进行赋值。
属性名称一致,类型不一致时mapstruct对部分数据也做了自动映射。
- 基本类型及其他们对应的包装类型。
- 基本类型的包装类型和String类型之间
- String类型和枚举类型之间
案例:
@Test
void moreDataSource(){
UserDto userDTO = new UserDto("张三",10,new Date());
AddressDTO addressDTO = new AddressDTO("中国","北京","北京");
UserPo userPo= springUserMapper.to(userDTO, addressDTO);
System.out.println(userPo);
}
---------------------
UserPo{name='张三', age=10, createTime=Tue Mar 22 16:18:44 CST 2022, city='北京', mycountry='中国'}
假设所有人的国家都是【中国】,那么可以通过设置固定值来实现。
//old
@Mapping(source = "addressDTO.country",target = "mycountry")
//new 两种方式都可以看场景选择
@Mapping(target = "mycountry",constant = "中国")
忽略字段:赋值时指定字段不做映射
@Mapping(target = "createTime",ignore = true)
日期转字符串:
@Mapping(source="createTime",target = "createTime",dateFormat = "yyyy-MM-dd hh:mm:ss")
对象互转:A转B->B转A
@Mapper(componentModel="spring")
public interface UserMapper{
@Mappings({
@Mapping(source="name",target = "nickname"),
@Mapping(source="createTime",target = "createTime",dateFormat = "yyyy-MM-dd hh:mm:ss")
})
UserDto1 to(UserDto userDto);
@InheritInverseConfiguration(name = "to")
UserDto from(UserDto1 dto1);
}
@Test
void reverse(){
UserDto userDto = new UserDto("zhangsan",27,new Date());
UserDto1 to = userMapper.to(userDto);
System.out.println(to);
UserDto form = userMapper.from(to);
System.out.println(form);
}
UserDto1{ nickname='zhangsan', age=27, createTime='2022-03-22 04:52:42'}
UserDto{name='zhangsan', age=27, createTime=Tue Mar 22 04:52:42 CST 2022}
通用父类:抽取通用的方法定义至父类,直接继承使用
public interface BaseMapper<SOURCE, TARGET> {
TARGET to(SOURCE var1);
List<TARGET> to(List<SOURCE> var1);
SOURCE from(TARGET var1);
List<SOURCE> from(List<TARGET> var1);
}
@Mapper(componentModel="spring")
public interface User1Mapper extends BaseMapper<UserDto, UserDto1> {
//存在定制属性时通过重写实现自己的需求
@Mappings({
@Mapping(source = "createTime",target = "createTime",dateFormat = "yyyyMMdd"),
@Mapping(source = "name",target = "nickname")
})
@Override
UserDto1 to(UserDto var1);
}
@Test
void testlist(){
UserDto userDTO = new UserDto("张三",10,new Date());
//时间转字符串
UserDto1 userDto1= user1Mapper.to(userDTO);
System.out.println(userDto1);
List<UserDto> userDTOS = new ArrayList<UserDto>(){{add(userDTO);}};
//***集合转集合 -> user1Mapper中并没有集合转换的方法,使用继承自父类BaseMapper的方法
List<UserDto1> userDto1s = user1Mapper.to(userDTOS);
System.out.println(userDto1s);
}
-----------------------------------------------------------------
UserDto1{ nickname='张三', age=10, createTime='20220322'}
[UserDto1{ nickname='张三', age=10, createTime='20220322'}]
对象转字符串:person中存在Homeaddress对象,但是personDto存储的地址是String可通过@mapping中的expression来实现转换
public class PersonDO {
private Integer id;
private String name;
private int age;
private Date birthday;
private String gender;
private String address;
}
public class PersonDTO {
private String userName;
private Integer age;
private Date birthday;
private Gender gender;
private HomeAddress address;
}
public class HomeAddress {
private String city;
private String street;
}
@Mapper(componentModel="spring")
public interface PersonConverter {
@Mapping(source = "name", target = "userName")
@Mapping(target = "address",expression = "java(stringToHomeAddress(person.getAddress()))")
PersonDTO do2dto(PersonDO person);
default HomeAddress stringToHomeAddress(String address){
//JSON.parseObject(address,HomeAddress.class)
String[] split = address.split("-");
HomeAddress homeAddress = new HomeAddress(split[0],split[1]);
return homeAddress;
}
@Mapping(source = "userName", target = "name")
@Mapping(target = "address",expression = "java(homeAddressToString(dto2do.getAddress()))")
PersonDO dto2do(PersonDTO dto2do);
default String homeAddressToString(HomeAddress address){
//JSON.toJSONString(address);
return address.getCity()+"-"+address.getStreet();
}
}
@Test
void test(){
HomeAddress homeAddress = new HomeAddress("shanghai","jutailu");
PersonDTO personDTO = new PersonDTO("chensan",20,new Date(),PersonDTO.Gender.MALE,homeAddress);
PersonDO personDO = personConverter.dto2do(personDTO);
System.out.println(personDO);
PersonDTO personDTO1 = personConverter.do2dto(personDO);
System.err.println(personDTO1);
}
--------------------------------------------------------------
PersonDTO{userName='chensan', age=20, birthday=Tue Mar 22 17:16:30 CST 2022, gender=MALE, address=HomeAddress{city='shanghai', street='jutailu'}}
PersonDO{id=null, name='chensan', age=20, birthday=Tue Mar 22 17:16:30 CST 2022, gender='MALE', address='shanghai-jutailu'}
更新bean映射:有些情况下不需要映射转换产生新的bean,而是更新已有的bean。
@Mapping(source = "name", target = "nickname")
void updateDTOFromDto1(UserDto userDto, @MappingTarget UserDto1 dto);
Map映射:map的值转换成指定格式的字符串
@MapMapping(valueDateFormat = "dd.MM.yyyy")
Map<String, String> longDateMapToStringStringMap(Map<Long, Date> source);
默认值设置:
@Mapping(target = "longProperty", source = "longProp", defaultValue = "-1")
装饰器自定义映射:
在某些情况下,可能需要自定义生成的映射方法,例如在目标对象中设置无法由生成的方法实现设置的附加属性。
实现起来也很简单,用装饰器模式实现映射器的一个抽象类,在映射器Mapper中添加注解@DecoratedWith指向装饰器类,使用时还是正常调用。
@Mapper
@DecoratedWith(CarMapperDecorator.class)
public interface CarMapper {
CarMapper INSTANCE = Mappers.getMapper(CarMapper.class);
@Mapping(source = "numberOfSeats", target = "seatCount")
CarDTO CarToCarDTO(Car car);
}
public abstract class CarMapperDecorator implements CarMapper {
private final CarMapper delegate;
protected CarMapperDecorator(CarMapper delegate) {
this.delegate = delegate;
}
@Override
public CarDTO CarToCarDTO(Car car) {
CarDTO dto = delegate.CarToCarDTO(car);
dto.setMakeInfo(car.getMake() + " " + new SimpleDateFormat( "yyyy-MM-dd" ).format(car.getCreateDate()));
return dto;
}
}
|