mapstruct对象复制&转换
Posted justry_deng
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了mapstruct对象复制&转换相关的知识,希望对你有一定的参考价值。
mapstruct对象复制&转换
简介
从功能上来讲,mapstruct
是一款类似于BeanUtils.copyProperties(Object source, Object target)
一样,实现对象属性值复制的;从实现上来讲,mapstruct
是一款类似于lombok
,基于你给出的方法入参出参模型及方法、类上的相关辅助注解,直接在编译时生成对应的属性值转换实现类。因为mapstruct
是使用自动生成的代码实现的对象属性值赋值(而不是像BeanUtils
一样采用反射获取值赋值),所以性能更快、效率更高。更多详见官网。
使用步骤简述
第一步:引入相关依赖
...
<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>
<!-- 此处理器用于 在编译时生成具体的Mapper实现 -->
<artifactId>mapstruct-processor</artifactId>
<version>$org.mapstruct.version</version>
</path>
<path>
<!-- 如果项目中还是用到了lombok,那么也需要加上lombok处理器声明 -->
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>$lombok.version</version>
</path>
</annotationProcessorPaths>
<!-- 编译时输出mapstruct的详细信息 start -->
<showWarnings>true</showWarnings>
<compilerArgs>
<arg>
-Amapstruct.verbose=true
</arg>
</compilerArgs>
<!-- 编译时输出mapstruct的详细信息 end -->
</configuration>
</plugin>
</plugins>
</build>
...
第二步:定义Mapper转换器
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
@Mapper
public interface CarMapper
@Mapping(target = "seatCount", source = "numberOfSeats")
CarDto carToCarDto(Car car);
或
@Mapper
public abstract class CarMapper
@Mapping(target = "seatCount", source = "numberOfSeats")
public abstract CarDto carToCarDto(Car car);
第三步:使用Mapper转换器
public static void main(String[] args) throws IOException
// source对象
Car car = new Car();
car.setNumberOfSeats(123);
car.setColor("白色");
// 获取mapper实例
CarMapper mapper = Mappers.getMapper(CarMapper.class);
// 调用对应方法,实现转换
CarDto carDto = mapper.carToCarDto(car);
// 输出: CarDto(seatCount=123, color=白色)
System.out.println(carDto);
获取Mapper实例的方式
注:获取Mapper实例的方式,取决于
@Mapper(componentModel=xxx)
中,componentModel
的模式:
default
:the mapper uses no component model, instances are typically retrieved viaMappers.getMapper(Class)
cdi
模式:the generated mapper is an application-scoped CDI bean and can be retrieved via@Inject
spring
模式:the generated mapper is a Spring bean and can be retrieved via@Autowired
jsr330
模式:the generated mapper is annotated with @javax.inject.Named and @Singleton, and can be retrieved via@Inject
default模式
default时,可通过 Mappers.getMapper(Class)
获取实例
@Mapper
//等价于@Mapper(componentModel = "default")
public interface CarMapper
@Mapping(target = "seatCount", source = "numberOfSeats")
CarDto carToCarDto(Car car);
获取实例
// 获取mapper实例
CarMapper mapper1 = Mappers.getMapper(CarMapper.class);
// 输出: com.szlaozicl.mybatisplusdemo.tmp.CarMapperImpl@69e308c6
System.out.println(mapper1);
CarMapper mapper2 = Mappers.getMapper(CarMapper.class);
// 输出: com.szlaozicl.mybatisplusdemo.tmp.CarMapperImpl@1a1ed4e5
System.out.println(mapper2);
注:
Mappers.getMapper(Class)
获取实例时,每次都是获取到一个新的实例。所以如果非要使用Mappers.getMapper(Class)
的话,需要尽量避免重复创建,可以使用下述方式:@Mapper public interface CarMapper /** 全局使用这一个对象 */ CarMapper INSTANCE = Mappers.getMapper(CarMapper.class); @Mapping(target = "seatCount", source = "numberOfSeats") CarDto carToCarDto(Car car);
或者
@Mapper public abstract class BusMapper /** 全局使用这一个对象 */ public final BusMapper INSTANCE = Mappers.getMapper(BusMapper.class); @Mapping(target = "seatCount", source = "numberOfSeats") public abstract CarDto carToCarDto(Car car);
spring模式
spring模式时,可通过 Mappers.getMapper(Class)
或@Autowired
或@Resource
等方式获取实例
@Mapper(componentModel = "spring")
public interface CarMapper
@Mapping(target = "seatCount", source = "numberOfSeats")
CarDto carToCarDto(Car car);
获取实例
@SpringBootApplication
public class SpringBootDemoApplication implements ApplicationRunner
@Autowired
private CarMapper carMapper1;
@Resource
private CarMapper carMapper2;
public static void main(String[] args) throws IOException
SpringApplication.run(SpringBootDemoApplication.class, args);
@Override
public void run(ApplicationArguments args) throws Exception
// 获取mapper实例
CarMapper mapper = Mappers.getMapper(CarMapper.class);
// 输出: com.szlaozicl.mybatisplusdemo.tmp.CarMapperImpl@f2c488
System.out.println(mapper);
// 输出: com.szlaozicl.mybatisplusdemo.tmp.CarMapperImpl@54acff7d
System.err.println(carMapper1);
// 输出: com.szlaozicl.mybatisplusdemo.tmp.CarMapperImpl@54acff7d
System.err.println(carMapper2);
cdi模式和jsr330模式
使用较少,不作介绍,详见官网。
常用知识点
source是转化源,target是转化目标
target是新对象
@Mapper(componentModel = "spring")
public interface CarMapper
/** Car为source, CarDto为target */
CarDto carToCarDto(Car car);
注:target是一个新创建的对象。
target是已有对象
@Mapper(componentModel = "spring")
public interface CarMapper
/** Car为source, CarDto为target */
void carToCarDto(Car car, @MappingTarget CarDto carDto);
字段名不同时
通过@Mapping
的source
与target
指定字段名映射
@Mapper(componentModel = "spring")
public interface CarMapper
/** 指定不同字段名间的映射 */
@Mapping(target = "seatCount", source = "numberOfSeats")
CarDto carToCarDto(Car car);
注:默认的,mapstruct只会转换字段名称相同的字段。
指定默认值
@Mapper
public interface CarMapper
CarMapper INSTANCE = Mappers.getMapper(CarMapper.class);
/**
* 当目标字段值最后为null前, 设置其默认值为100
*/
@Mapping(target = "seatCount", defaultValue = "100")
CarDto carToCarDto(Car car);
常量值
@Mapper
public interface CarMapper
CarMapper INSTANCE = Mappers.getMapper(CarMapper.class);
/**
* 指定常量值
*/
@Mapping(target = "seatCount", constant = "100")
CarDto carToCarDto(Car car);
多个字段映射
@Mapper
public interface CarMapper
@Mapping(target = "length", source = "carLength")
@Mapping(target = "seatCount", source = "numberOfSeats")
CarDto carToCarDto(Car car);
或
@Mapper
public interface CarMapper
@Mappings(value =
@Mapping(target = "length", source = "carLength"),
@Mapping(target = "seatCount", source = "numberOfSeats")
)
CarDto carToCarDto(Car car);
多级字段定位
可通过字段名
.字段名
.字段名
的形式进行多级定位
public class multiLevel_field
public static void main(String[] args)
Car car = new Car();
car.setColor("yellow");
car.setNumberOfSeats(10);
car.setPerson(new Person("张三", 28));
// 输出:multiLevel_field.CarDto(color=yellow, seatCount=10, personName=张三, personAge=28)
CarDto carDto = CarMapper.INSTANCE.carToCarDto(car);
System.out.println(carDto);
// 输出:multiLevel_field.Car(color=yellow, numberOfSeats=10, person=multiLevel_field.Person(name=张三, age=28))
car = CarMapper.INSTANCE.carDtoToCar(carDto);
System.out.println(car);
@Mapper
public interface CarMapper
CarMapper INSTANCE = Mappers.getMapper(CarMapper.class);
@Mapping(target = "seatCount", source = "numberOfSeats")
@Mapping(target = "personName", source = "person.name")
@Mapping(target = "personAge", source = "person.age")
CarDto carToCarDto(Car c);
@Mapping(target = "numberOfSeats", source = "seatCount")
@Mapping(target = "person.name", source = "personName")
@Mapping(target = "person.age", source = "personAge")
Car carDtoToCar(CarDto c);
@Data
public static class CarDto
private String color;
private Integer seatCount;
private String personName;
private Integer personAge;
@Data
public static class Car
private String color;
private Integer numberOfSeats;
private Person person;
@Data
@AllArgsConstructor
public static class Person
private String name;
private Integer age;
多个source
通过多级定位,mapstruct可以实现多个对象转一个对象
public class multi_to_one
public static void main(String[] args)
Car car = new Car();
car.setColor("green");
car.setNumberOfSeats(10);
Person person = new Person("张三", 28);
// 输出:multi_to_one.CarDto(color=green, seatCount=10, personName=张三, personAge=28)
CarDto carDto1 = CarMapper.INSTANCE.generateCarDto1(car, person);
System.out.println(carDto1);
// 输出:multi_to_one.CarDto(color=green, seatCount=10, personName=张三, personAge=28)
CarDto carDto2 = new CarDto();
CarMapper.INSTANCE.generateCarDto2(car, person, carDto2);
System.out.println(carDto2);
@Mapper
public interface CarMapper
CarMapper INSTANCE = Mappers.getMapper(CarMapper.class);
/**
* 因为Car和Person中所有的字段里,只有一个与CarDto#color匹配,所以这里可以省略该@Mapping。
* 但如果有多个同事匹配时,需要指定@Mapping;否则编译时mapstruct会报错
*/
@Mapping(target = "seatCount", source = "c.numberOfSeats")
@Mapping(target = "personName", source = "p.name")
@Mapping(target = "personAge", source = "p.age")
CarDto generateCarDto1(Car c, Person p);
/**
* 等价于
*/
@Mapping(target = "seatCount", source = "c.numberOfSeats")
@Mapping(target = "personName", source = "p.name")
@Mapping(target = "personAge", source = "p.age")
void generateCarDto2(Car c, Person p, @MappingTarget CarDto carDto);
@Data
public static class CarDto
private String color;
private Integer seatCount;
private String personName;
private Integer personAge;
@Data
public static class Car
private String color;
private Integer numberOfSeats;
@Data
@AllArgsConstructor
public static class Person
private String name;
private Integer age;
字段大小写不同时
mapstruct是基于getter/setter方法读写字段,因为java getter/setter是小驼峰式命名,所以对于字段的首字母的大小写不敏感,能赋值成功,如下面的color与Color,但是对于其它位置的大小写敏感,不能赋值成功,如下面的size与siZe;对于is打头的boolean型字段,lombok生成的getter/setter是回保留原有的is的,所以mapstruct解析后girl与isGirl是不匹配的,除非你自己额外添加对应的getter
以上是关于mapstruct对象复制&转换的主要内容,如果未能解决你的问题,请参考以下文章