博主 默语带您 Go to New World. ✍ 个人主页—— 默语 的博客👦🏻 《java 面试题大全》 🍩惟余辈才疏学浅,临摹之作或有不妥之处,还请读者海涵指正。☕🍭 《MYSQL从入门到精通》数据库是开发者必会基础之一~ 🪁 吾期望此文有资助于尔,即使粗浅难及深广,亦备添少许微薄之助。苟未尽善尽美,敬请批评指正,以资改进。!💻⌨
在我们日常开发的程序中,为了各层之间解耦,一般会定义不同的对象用来在不同层之间传递数据,比如xxxDTO、xxxVO、xxxQO,当在不同层之间传输数据时,不可避免地经常需要将这些对象进行相互转换。
今天给大家介绍一个对象转换工具MapStruct,代码简洁安全、性能高,强烈推荐。
MapStruct是一个代码生成器,它基于约定优于配置,极大地简化了Java Bean类型之间映射的实现。特点如下:
1.基于注解 2.在编译期自动生成映射转换代码 3.类型安全、高性能、无依赖性、易于理解阅读
Car.java
public class Car {
private String make;
private int numberOfSeats;
private CarType type;
//constructor, getters, setters etc.
}
CarDto .java
public class CarDto {
private String make;
private int seatCount;
private String type;
//constructor, getters, setters etc.
}
@Mapper (1:下面对此解释)
public interface CarMapper {
CarMapper INSTANCE = Mappers.getMapper( CarMapper.class ); (3:下面对此解释)
@Mapping(source = "numberOfSeats", target = "seatCount")
CarDto carToCarDto(Car car); (2:下面对此解释)
}
注释@Mapper(1)将接口标记为映射接口,并允许 MapStruct 处理器在编译期间启动。
实际的映射方法2期望源对象作为参数并返回目标对象。它的名字可以自由选择。
对于源对象和目标对象中具有不同名称的属性,可以使用注释来配置名称。@Mapping
在需要和可能的情况下,将为源和目标中具有不同类型的属性执行类型转换,例如,属性将从枚举类型转换为字符串。type
当然,一个接口中可以有多个映射方法,所有这些方法的实现都将由MapStruct生成。
可以从类中检索接口实现的实例。按照惯例,接口声明一个成员MappersINSTANCE 3,为客户端提供对映射器实现的访问。
shouldMapCarToDto.java
@Test
public void shouldMapCarToDto() {
//given
Car car = new Car( "Morris", 5, CarType.SEDAN );
//when
CarDto carDto = CarMapper.INSTANCE.carToCarDto( car );
//then
assertThat( carDto ).isNotNull();
assertThat( carDto.getMake() ).isEqualTo( "Morris" );
assertThat( carDto.getSeatCount() ).isEqualTo( 5 );
assertThat( carDto.getType() ).isEqualTo( "SEDAN" );
}
如想了解更多请查看官网内容(https://mapstruct.org/)
这里使用Gradle构建
dependencies {
implementation 'org.mapstruct:mapstruct:1.4.2.Final'
annotationProcessor 'org.mapstruct:mapstruct-processor:1.4.2.Final'
}
maven仓库地址;
// https://mvnrepository.com/artifact/org.mapstruct/mapstruct-processor
implementation group: 'org.mapstruct', name: 'mapstruct-processor', version: '1.4.2.Final'
maven地址:
<!-- https://mvnrepository.com/artifact/org.mapstruct/mapstruct-processor -->
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>1.4.2.Final</version>
</dependency>
创建两个示例对象(EG: 将Demo对象转换为DemoDto对象) 保证对象之间的值是相同的;
/**
* 源对象
*/
@Data
public class Demo {
private Integer id;
private String name;
}
/**
* 目标对象
*/
@Data
public class DemoDto {
private Integer id;
private String name;
}
只需要创建一个转换器接口类,并在类上添加 @Mapper 注解即可(官方示例推荐以 xxxMapper 格式命名转换器名称)
@Mapper
public interface DemoMapper {
//使用Mappers工厂获取DemoMapper实现类
DemoMapper INSTANCE = Mappers.getMapper(DemoMapper.class);
//定义接口方法,参数为来源对象,返回值为目标对象
DemoDto toDemoDto(Demo demo);
}
public static void main(String[] args) {
Demo demo = new Demo();
demo.setId(111);
demo.setName("hello");
DemoDto demoDto = DemoMapper.INSTANCE.toDemoDto(demo);
System.out.println("目标对象demoDto为:" + demoDto);
//输出结果:目标对象demoDto为:DemoDto(id=111, name=hello)
}
测试结果如下:
目标对象demoDto为:DemoDto(id=111, name=hello)
达到了我们的预期结果。
为什么声明一个接口就可以转换对象呢? 我们看一下MapStruct在编译期间自动生成的实现类:
@Generated(
value = "org.mapstruct.ap.MappingProcessor",
date = "2022-09-01T17:54:38+0800",
comments = "version: 1.4.2.Final, compiler: IncrementalProcessingEnvironment from gradle-language-java-7.3.jar, environment: Java 1.8.0_231 (Oracle Corporation)"
)
public class DemoMapperImpl implements DemoMapper {
@Override
public DemoDto toDemoDto(Demo demo) {
if ( demo == null ) {
return null;
}
DemoDto demoDto = new DemoDto();
demoDto.setId( demo.getId() );
demoDto.setName( demo.getName() );
return demoDto;
}
}
可以看到,MapStruct帮我们将繁杂的代码自动生成了,而且实现类中用的都是最基本的get、set方法,易于阅读理解,转换速度非常快。
上面的例子只是小试牛刀,下面开始展示MapStruct的强大之处。
(限于篇幅,这里不展示自动生成的实现类和验证结果,大家可自行测试)
场景1:属性名称不同、(基本)类型不同
关键字:@Mapping注解
/**
* 来源对象
*/
@Data
public class Demo {
private Integer id;
private String name;
}
/**
* 目标对象
*/
@Data
public class DemoDto {
private String id;
private String fullname;
}
/**
* 转换器
*/
@Mapper
public interface DemoMapper {
DemoMapper INSTANCE = Mappers.getMapper(DemoMapper.class);
@Mapping(target = "fullname", source = "name")
DemoDto toDemoDto(Demo demo);
}
场景2:统一映射不同类型 下面例子中,time1、time2、time3都会被转换,具体说明看下面的注释:
/**
* 来源对象
*/
@Data
public class Demo {
private Integer id;
private String name;
/**
* time1、time2名称相同,time3转为time33
* 这里的time1、time2、time33都是Date类型
*/
private Date time1;
private Date time2;
private Date time3;
}
/**
* 目标对象
*/
@Data
public class DemoDto {
private String id;
private String name;
/**
* 这里的time1、time2、time33都是String类型
*/
private String time1;
private String time2;
private String time33;
}
/**
* 转换器
*/
@Mapper
public interface DemoMapper {
DemoMapper INSTANCE = Mappers.getMapper(DemoMapper.class);
@Mapping(target = "time33", source = "time3")
DemoDto toDemoDto(Demo demo);
//MapStruct会将所有匹配到的:
//源类型为Date、目标类型为String的属性,
//按以下方法进行转换
static String date2String(Date date) {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String strDate = simpleDateFormat.format(date);
return strDate;
}
}
场景3:固定值、忽略某个属性、时间转字符串格式 一个例子演示三种用法,具体说明看注释,很容易理解:
关键字:ignore、constant、dateFormat
/**
* 来源对象
*/
@Data
public class Demo {
private Integer id;
private String name;
private Date time;
}
/**
* 目标对象
*/
@Data
public class DemoDto {
private String id;
private String name;
private String time;
}
/**
* 转换器
*/
@Mapper
public interface DemoMapper {
DemoMapper INSTANCE = Mappers.getMapper(DemoMapper.class);
//id属性不赋值
@Mapping(target = "id", ignore = true)
//name属性固定赋值为“hello”
@Mapping(target = "name", constant = "hello")
//time属性转为yyyy-MM-dd HH:mm:ss格式的字符串
@Mapping(target = "time", dateFormat = "yyyy-MM-dd HH:mm:ss")
DemoDto toDemoDto(Demo demo);
}
场景4:为某个属性指定转换方法 场景2中,我们是按照某个转换方法,统一将一种类型转换为另外一种类型;而下面这个例子,是为某个属性指定方法:
关键字:@Named注解、qualifiedByName
/**
* 来源对象
*/
@Data
public class Demo {
private Integer id;
private String name;
}
/**
* 目标对象
*/
@Data
public class DemoDto {
private String id;
private String name;
}
/**
* 转换器
*/
@Mapper
public interface DemoMapper {
DemoMapper INSTANCE = Mappers.getMapper(DemoMapper.class);
//为name属性指定@Named为convertName的方法进行转换
@Mapping(target = "name", qualifiedByName = "convertName")
DemoDto toDemoDto(Demo demo);
@Named("convertName")
static String aaa(String name) {
return "姓名为:" + name;
}
}
场景5:多个参数合并为一个对象 如果参数为多个的话,@Mapping注解中的source就要指定是哪个参数了,用点分隔:
关键字:点(.)
/**
* 来源对象
*/
@Data
public class Demo {
private Integer id;
private String name;
}
/**
* 目标对象
*/
@Data
public class DemoDto {
private String fullname;
private String timestamp;
}
/**
* 转换器
*/
@Mapper
public interface DemoMapper {
DemoMapper INSTANCE = Mappers.getMapper(DemoMapper.class);
//fullname属性赋值demo对象的name属性(注意这里.的用法)
//timestamp属性赋值为传入的time参数
@Mapping(target = "fullname", source = "demo.name")
@Mapping(target = "timestamp", source = "time")
DemoDto toDemoDto(Demo demo, String time);
}
场景6:已有目标对象,将源对象属性覆盖到目标对象
覆盖目标对象属性时,一般null值不覆盖,所以需要在类上的@Mapper注解中添加属性:nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE 代表null值不进行赋值。
关键字:@MappingTarget注解、nullValuePropertyMappingStrategy
/**
* 来源对象
*/
@Data
public class Demo {
private Integer id;
private String name;
}
/**
* 目标对象
*/
@Data
public class DemoDto {
private String id;
private String name;
}
/**
* 转换器
*/
@Mapper(unmappedTargetPolicy = ReportingPolicy.IGNORE,
nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE)
public interface DemoMapper {
DemoMapper INSTANCE = Mappers.getMapper(DemoMapper.class);
//将已有的目标对象当作一个参数传进来
DemoDto toDemoDto(Demo demo, @MappingTarget DemoDto dto);
}
场景7:源对象两个属性合并为一个属性
这种情况可以使用@AfterMapping注解。
关键字:@AfterMapping注解、@MappingTarget注解
/**
* 来源对象
*/
@Data
public class Demo {
private Integer id;
private String firstName;
private String lastName;
}
/**
* 目标对象
*/
@Data
public class DemoDto {
private String id;
private String name;
}
/**
* 转换器
*/
@Mapper
public interface DemoMapper {
DemoMapper INSTANCE = Mappers.getMapper(DemoMapper.class);
DemoDto toDemoDto(Demo demo);
//在转换完成后执行的方法,一般用到源对象两个属性合并为一个属性的场景
//需要将源对象、目标对象(@MappingTarget)都作为参数传进来,
@AfterMapping
static void afterToDemoDto(Demo demo, @MappingTarget DemoDto demoDto) {
String name = demo.getFirstName() + demo.getLastName();
demoDto.setName(name);
}
}
本文介绍了对象转换工具 MapStruct 库,以安全、简洁、优雅的方式来优化我们的转换代码。
从文中的示例场景中可以看出,MapStruct 提供了大量的功能和配置,使我们可以快捷的创建出各种或简单或复杂的映射器。而这些,也只是 MapStruct 库的冰山一角,还有很多强大的功能文中没有提到,感兴趣的朋友可以自行查看官方文档。
pom 配置:
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<org.mapstruct.version>1.4.1.Final</org.mapstruct.version>
<org.projectlombok.version>1.18.12</org.projectlombok.version>
</properties>
<dependencies>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
<version>${org.mapstruct.version}</version>
</dependency>
<!-- lombok dependencies should not end up on classpath -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${org.projectlombok.version}</version>
<scope>provided</scope>
</dependency>
<!-- idea 2018.1.1 之前的版本需要添加下面的配置,后期的版本就不需要了,可以注释掉,
我自己用的2019.3 -->
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>${org.mapstruct.version}</version>
<scope>provided</scope>
</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.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${org.projectlombok.version}</version>
</path>
<path>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>${org.mapstruct.version}</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
</plugins>
</build>
关于lombok和mapstruct的版本兼容问题多说几句,maven插件要使用3.6.0版本以上、lombok使用1.16.16版本以上,另外编译的lombok mapstruct的插件不要忘了加上。否则会出现下面的错误:
No property named "aaa" exists in source parameter(s). Did you mean "null"?
这种异常就是lombok编译异常导致缺少get setter方法造成的。还有就是缺少构造函数也会抛异常。
实体类:
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class Student {
private String name;
private int age;
private GenderEnum gender;
private Double height;
private Date birthday;
}
public enum GenderEnum {
Male("1", "男"),
Female("0", "女");
private String code;
private String name;
public String getCode() {
return this.code;
}
public String getName() {
return this.name;
}
GenderEnum(String code, String name) {
this.code = code;
this.name = name;
}
}
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class StudentVO {
private String name;
private int age;
private String gender;
private Double height;
private String birthday;
}
@Mapper
public interface StudentMapper {
StudentMapper INSTANCE = Mappers.getMapper(StudentMapper.class);
@Mapping(source = "gender.name", target = "gender")
@Mapping(source = "birthday", target = "birthday", dateFormat = "yyyy-MM-dd HH:mm:ss")
StudentVO student2StudentVO(Student student);
}
实体类是开发过程少不了的,就算是用工具生成肯定也是要有的,需要手写的部分就是这个Mapper的接口,编译完成后会自动生成相应的实现类(targer)
然后就可以直接用mapper进行实体的转换了
public class Test {
public static void main(String[] args) {
Student student = Student.builder().name("小明").age(6).gender(GenderEnum.Male).height(121.1).birthday(new Date()).build();
System.out.println(student);
//这行代码便是实际要用的代码
StudentVO studentVO = StudentMapper.INSTANCE.student2StudentVO(student);
System.out.println(studentVO);
}
}
mapper可以进行字段映射,改变字段类型,指定格式化的方式,包括一些日期的默认处理。
可以手动指定格式化的方法:
@Mapper
public interface StudentMapper {
StudentMapper INSTANCE = Mappers.getMapper(StudentMapper.class);
@Mapping(source = "gender", target = "gender")
@Mapping(source = "birthday", target = "birthday", dateFormat = "yyyy-MM-dd HH:mm:ss")
StudentVO student2StudentVO(Student student);
default String getGenderName(GenderEnum gender) {
return gender.getName();
}
}
上面只是最简单的实体映射处理,下面介绍一些高级用法
属性映射基于上面的mapping配置
@Mapper
public interface StudentMapper {
StudentMapper INSTANCE = Mappers.getMapper(StudentMapper.class);
@Mapping(source = "gender.name", target = "gender")
@Mapping(source = "birthday", target = "birthday", dateFormat = "yyyy-MM-dd HH:mm:ss")
StudentVO student2StudentVO(Student student);
List<StudentVO> students2StudentVOs(List<Student> studentList);
}
public static void main(String[] args) {
Student student = Student.builder().name("小明").age(6).gender(GenderEnum.Male).height(121.1).birthday(new Date()).build();
List<Student> list = new ArrayList<>();
list.add(student);
List<StudentVO> result = StudentMapper.INSTANCE.students2StudentVOs(list);
System.out.println(result);
}
2.多对象转换到一个对象
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class Student {
private String name;
private int age;
private GenderEnum gender;
private Double height;
private Date birthday;
}
@Data
@AllArgsConstructor
@Builder
@NoArgsConstructor
public class Course {
private String courseName;
private int sortNo;
private long id;
}
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class StudentVO {
private String name;
private int age;
private String gender;
private Double height;
private String birthday;
private String course;
}
@Mapper
public interface StudentMapper {
StudentMapper INSTANCE = Mappers.getMapper(StudentMapper.class);
@Mapping(source = "student.gender.name", target = "gender")
@Mapping(source = "student.birthday", target = "birthday", dateFormat = "yyyy-MM-dd HH:mm:ss")
@Mapping(source = "course.courseName", target = "course")
StudentVO studentAndCourse2StudentVO(Student student, Course course);
}
public class Test {
public static void main(String[] args) {
Student student = Student.builder().name("小明").age(6).gender(GenderEnum.Male).height(121.1).birthday(new Date()).build();
Course course = Course.builder().id(1L).courseName("语文").build();
StudentVO studentVO = StudentMapper.INSTANCE.studentAndCourse2StudentVO(student, course);
System.out.println(studentVO);
}
}
3.默认值
@Mapper
public interface StudentMapper {
StudentMapper INSTANCE = Mappers.getMapper(StudentMapper.class);
@Mapping(source = "student.gender.name", target = "gender")
@Mapping(source = "student.birthday", target = "birthday", dateFormat = "yyyy-MM-dd HH:mm:ss")
@Mapping(source = "course.courseName", target = "course")
@Mapping(target = "name", source = "student.name", defaultValue = "张三")
StudentVO studentAndCourse2StudentVO(Student student, Course course);
}
<dependency>
<groupId>net.sf.dozer</groupId>
<artifactId>dozer</artifactId>
<version>5.5.1</version>
</dependency>
然后分别创建一个UserVo和UserDto,并对他们做转换
首先让两个类的属性名称一样,如下
@Data
public class UserVo {
private String name;
private int id;
}
@Data
public class UserDto {
private String name;
private int id;
}
接下来怎么转换呢,代码如下
Mapper dozerMapper = new DozerBeanMapper();
UserVo userVo = new UserVo();
userVo.setName("mg");
userVo.setId(10);
InUserDto userDto = dozerMapper.map(userVo,UserDto.class);
System.out.println(userDto.getName());
输出结果为
mg
UserVo转换为UserDto,这是属性完全相同的情况,不过通常属性名称都是不同的,那怎么办呢
修改UserVo 为
@Data
public class UserVo {
private String sname;
private int sid;
}
@Mapping(“实体类对应名字”) 这样就可以解决对应的实体类个别不一样的问题;
修改UserDto为
@Data
public class UserDto {
@Mapping("sname")
private String name;
@Mapping("sid")
private int id;
}
在执行下刚写的转换函数,试试是否能成功转换了
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.1</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example.DemoCrud</groupId>
<artifactId>democurd</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>democurd</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- mybatis整合Springboot -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.2</version>
</dependency>
<dependency>
<!-- maven 添加json-->
<groupId>net.sf.json-lib</groupId>
<artifactId>json-lib</artifactId>
<version>2.4</version>
</dependency>
<!--jquery-webjar-->
<dependency>
<groupId>org.webjars</groupId>
<artifactId>jquery</artifactId>
<version>3.3.0</version>
</dependency>
<!--bootstrap-webjar-->
<dependency>
<groupId>org.webjars</groupId>
<artifactId>bootstrap</artifactId>
<version>4.0.0</version>
</dependency>
<!--配置文件注入时使用后会有提示-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.18</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.3.1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.12</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.47</version>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.3.2</version>
</dependency>
<!--mapStruct依赖-->
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-jdk8</artifactId>
<version>1.2.0.CR1</version>
</dependency>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>1.2.0.CR1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
<version>1.3.1.Final</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectors</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
<!-- 添加资源 -->
<resources>
<resource>
<directory>src/main/resources</directory>
<!-- src/main/resources下的指定资源放行 -->
<includes>
<include>**/*.properties</include>
<include>**/*.yml</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>
</build>
</project>
代码结构:
先创建2个实体类:
package com.example.democrud.democurd.DoVO;
import lombok.Data;
/**
* 来源对象
*/
@Data
public class DoUser {
private String id;
private String user;
private String age;
}
package com.example.democrud.democurd.DoVO;
import lombok.Data;
/**
* 目标对象
*/
@Data
public class VoUser {
private String id;
private String user;
private String age;
}
为了加深理解我们先手写一个映射关系;
@Mapper
public interface CarMap {
CarMap INSTANCE = Mappers.getMapper( CarMap.class );
DoUser toVoUser(VoUser demo);
}
实现类
package com.example.democrud.democurd.DoVO;
public class CarMapImpl implements CarMap{
@Override
public DoUser toVoUser(VoUser demo) {
if ( demo == null ) {
return null;
}
DoUser demoDto = new DoUser();
demoDto.setId( demo.getId() );
demoDto.setUser( demo.getUser() );
demoDto.setAge(demo.getAge());
return demoDto;
}
}
控制台:
public static void main(String[] args) {
VoUser user = new VoUser();
user.setUser("闫文超");
user.setAge("12");
user.setId("66");
DoUser doUser=CarMap.INSTANCE.toVoUser(user);
System.out.println("doUser==>"+doUser);
}
}
这样2个实体类的关系就会达成映射关系
还有一个简单的方法;(componentModel = “spring”) 加入注解就可以自动实现他的方法了(和上面 的区别只添加这个即可)
package com.example.democrud.democurd.DoVO;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
@Mapper(componentModel = "spring")
public interface CarMap {
CarMap INSTANCE = Mappers.getMapper( CarMap.class );
// DoUser toVoUser(VoUser demo);
DoUser toVoUsers(VoUser demo);
}
测试结果:
是不是很容易啊,这样既能分别定义的VO、PO、DTO,又能快速完成开发工作了;
实体类和上面相识;
重点 BaseVo 工具类:
package com.example.democrud.democurd.DoVO;
import java.io.Serializable;
import java.lang.reflect.InvocationTargetException;
import java.math.BigDecimal;
import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.beanutils.ConvertUtils;
import org.apache.commons.beanutils.converters.BigDecimalConverter;
import org.apache.commons.beanutils.converters.DoubleConverter;
import org.apache.commons.beanutils.converters.FloatConverter;
import org.apache.commons.beanutils.converters.IntegerConverter;
import org.apache.commons.beanutils.converters.LongConverter;
import org.apache.commons.beanutils.converters.ShortConverter;
/**
*
* VO基类
* @author clwang
* @version 2019年4月17日
*/
public class BaseVO implements Serializable {
/**
* 意义,目的和功能,以及被用到的地方<br>
*/
private static final long serialVersionUID = 7474374467497907429L;
/**
*
* PO转VO
* @param po po对象
* @param voClass VO的class对象
* @param <T> 具体VO对象的泛型
* @return 返回VO
*/
public static <T extends BaseVO> T toVO(Object po, Class<T> voClass) {
if (null == po) {
return null;
}
setDefaultValue();
try {
T vo = voClass.newInstance();
BeanUtils.copyProperties(vo, po);
return vo;
} catch (InstantiationException e) {
System.out.println(e);
} catch (IllegalAccessException e) {
System.out.println(e);
} catch (InvocationTargetException e) {
System.out.println(e);
}
return null;
}
/**
* BeanUtils空值拷贝默认属性设置,空值任然为空,不赋值默认值
*/
private static void setDefaultValue() {
ConvertUtils.register(new LongConverter(null), Long.class);
ConvertUtils.register(new IntegerConverter(null), Integer.class);
ConvertUtils.register(new ShortConverter(null), Short.class);
ConvertUtils.register(new FloatConverter(null), Float.class);
ConvertUtils.register(new DoubleConverter(null), Double.class);
ConvertUtils.register(new BigDecimalConverter(null), BigDecimal.class);
}
/**
*
* VO转PO
* @param poClass po class对象
* @param <T> 具体po对象的泛型
* @return PO对象
*/
public <T> T toPO(Class<T> poClass) {
setDefaultValue();
try {
T po = poClass.newInstance();
BeanUtils.copyProperties(po, this);
return po;
} catch (InstantiationException e) {
System.out.println(e);
} catch (IllegalAccessException e) {
System.out.println(e);
} catch (InvocationTargetException e) {
System.out.println(e);
}
return null;
}
}
我们看下如何调用:
public static void main(String[] args) {
VoUser user = new VoUser();
user.setUser("闫文超");
user.setAge("12");
user.setId("66");
VoUser user1 = new VoUser();
user1.setUser("闫22");
user1.setAge("33");
user1.setId("77");
test1(user);
// test2(user1);
}
public static void test1(VoUser vouser) {
//用实体类继承该f方法后调用
//前面是空数据需要导入数据的类 vouser是有数据的
DoUser douser = vouser.toPO(DoUser.class);
System.out.println("test1==>"+douser);
}
public static void test2(VoUser vouser) {
DoUser doUser=DoUser.toVO(vouser,DoUser.class);
System.out.println("test2==>"+doUser);
}
补充:
public static void test3(VoUser vouser) {
DoUser doUser=new DoUser();
//前面的为值 后面是需要赋值的实体类
BeanUtil.copyProperties(vouser,doUser);
System.out.println("test3==>"+doUser);
}
想用那种大家随意喽;