mapstruct 之 实体转换
MapStruct配置
简介:
1.在多模块多层级的开发中,每一层都有自己的数据模型,DO,DTO,VO等.
2.对象与对象之间进行转换,一般通过get,set方法.或者是BeanUtils进行转换,但是对象之间名称不同或者类型不同时,BeanUtils则不会进行处理,需要我们自己进行处理.
3.MapStruct是一个属性映射工具,基于注解进行开发,可以支持名称不同,类型不同等处理.
MapStruct配置
maven引入依赖:
<properties>
<mapstruct.version>1.3.0.Final</mapstruct.version>
</properties>
<!-- 实体映射工具类相关 -->
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-jdk8</artifactId>
<version>${mapstruct.version}</version>
</dependency>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>${mapstruct.version}</version>
</dependency>
简单使用
简单使用:
1.先定义一个接口:
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
@Mapper
public interface PersonConvert {
PersonConvert instance = Mappers.getMapper(PersonConvert.class);
/**
* DTO转DO
* @return
*/
PersonDO personDtoToPersonDO(PersonDTO dto);
}
注意这里的Mapper注解导入的包是 org.mapstruct.Mapper
2.定义两个实体:
public class PersonDO {
private String name;
private Integer age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}
public class PersonDTO {
private String name;
private Integer age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public PersonDTO(){}
public PersonDTO(String name, Integer age) {
this.name = name;
this.age = age;
}
}
进行测试:
public class Test {
public static void main(String[] args) {
PersonDTO personDTO = new PersonDTO("mapStruct",18);
PersonDO personDO = PersonConvert.instance.personDtoToPersonDO(personDTO);
System.out.println(personDO.getName()+":"+personDO.getAge());
}
}
测试结果:
我们通过class可以查看mapStruct的是如何进行转换的.
从图中得知,是通过实现我们的接口,然后调用get,set方法进行转换.
字段名称不一样进行转换
字段名称不一样进行转换.
这个时候需要我们添加一些额外的注解:
将DO的名字变更一下
public class PersonDO {
private String personName;
private Integer age;
public String getPersonName() {
return personName;
}
public void setPersonName(String personName) {
this.personName = personName;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}
接口也改一下:
@Mapper
public interface PersonConvert {
PersonConvert instance = Mappers.getMapper(PersonConvert.class);
/**
* DTO转DO
* @return
*/
@Mappings({
@Mapping(source = "name", target = "personName")
})
PersonDO personDtoToPersonDO(PersonDTO dto);
}
通过注解将名字映射一下.
测试结果:
字段名称类型不一样进行转换以及注意点
字段名称不一样,类型不一样进行转换
DTO
public class PersonDTO {
private String name;
private Integer age;
private Integer gender;
public PersonDTO(String name, Integer age, Integer gender) {
this.name = name;
this.age = age;
this.gender = gender;
}
public Integer getGender() {
return gender;
}
public void setGender(Integer gender) {
this.gender = gender;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public PersonDTO(){}
public PersonDTO(String name, Integer age) {
this.name = name;
this.age = age;
}
}
DO
public class PersonDO {
private String personName;
private Integer age;
private Boolean personGender;
public Boolean getPersonGender() {
return personGender;
}
public void setPersonGender(Boolean personGender) {
this.personGender = personGender;
}
public String getPersonName() {
return personName;
}
public void setPersonName(String personName) {
this.personName = personName;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}
接口也要相应的改一下:
@Mapper
public interface PersonConvert {
PersonConvert instance = Mappers.getMapper(PersonConvert.class);
/**
* DTO转DO
* @return
*/
@Mappings({
@Mapping(source = "name", target = "personName"),
@Mapping(source = "gender", target = "personGender",qualifiedByName = "integerToBoolean")
})
PersonDO personDtoToPersonDO(PersonDTO dto);
@Named("integerToBoolean")
default Boolean integerToBoolean(Integer value){
if (value == 1) {
return false;
}
return true;
}
}
qualifiedByName 注解和Named注解配合使用,代表这个字段的处理用指定的哪个方法. 这样可以帮我们解决类型不一致的问题.
测试:
public class Test {
public static void main(String[] args) {
PersonDTO personDTO = new PersonDTO("mapStruct",18,1);
PersonDO personDO = PersonConvert.instance.personDtoToPersonDO(personDTO);
System.out.println(personDO.getPersonName()+":"+personDO.getAge()+":"+personDO.getPersonGender());
}
}
查看class类是不是处理的时候调用了我们的方法.
可以看到class实现类调用的是我们自定义的方法.
对于一些通用的类型转换,我们可以自定义类:
也可以写成这样:
增加一个类:
public class TypeStrategy {
@Named("integerToBoolean")
public Boolean integerToBoolean(Integer value){
if (value == 1) {
return false;
}
return true;
}
}
接口处:
@Mapper(uses=TypeStrategy.class)
public interface PersonConvert {
PersonConvert instance = Mappers.getMapper(PersonConvert.class);
/**
* DTO转DO
* @return
*/
@Mappings({
@Mapping(source = "name", target = "personName"),
@Mapping(source = "gender", target = "personGender",qualifiedByName = "integerToBoolean")
})
PersonDO personDtoToPersonDO(PersonDTO dto);
}
这两种方式都是一样的效果.就是一个是调用接口默认的方法,一个是调用
@Mapper(uses=TypeStrategy.class)类里的方法.
注意点:
在我们使用多个实体转换多个实体的时候,
例如:
@Mapper(uses=TypeStrategy.class)
public interface PersonConvert {
PersonConvert instance = Mappers.getMapper(PersonConvert.class);
@Mappings({
@Mapping(source = "name", target = "personName"),
@Mapping(source = "gender", target = "personGender",qualifiedByName = "integerToBoolean")
})
List<PersonDO> personDtoListToPersonDOList(List<PersonDTO> dtoList);
像这样的是不对的:
可以看到运行的时候都会报错,说找不到该属性.这是工作中踩的坑.
而是应该将注解在单个实体中映射.
@Mapper(uses=TypeStrategy.class)
public interface PersonConvert {
PersonConvert instance = Mappers.getMapper(PersonConvert.class);
List<PersonDO> personDtoListToPersonDOList(List<PersonDTO> dtoList);
/**
* DTO转DO
* @return
*/
@Mappings({
@Mapping(source = "name", target = "personName"),
@Mapping(source = "gender", target = "personGender",qualifiedByName = "integerToBoolean")
})
PersonDO personDtoToPersonDO(PersonDTO dto);
}
测试类:
public class Test {
public static void main(String[] args) {
List<PersonDTO> listDTO = new LinkedList<>();
PersonDTO personDTO2 = new PersonDTO("mapStruct2",19,0);
PersonDTO personDTO = new PersonDTO("mapStruct",18,1);
listDTO.add(personDTO);
listDTO.add(personDTO2);
List<PersonDO> personDOList = PersonConvert.instance.personDtoListToPersonDOList(listDTO);
personDOList.forEach(personDO->
System.out.println(personDO.getPersonName()+":"+personDO.getAge()
+":"+personDO.getPersonGender()));
}
}
结果:
可以查看class中生成的代码:
也是通过循环的方式去调用的单个实体的转换.