• PO VO DTO 转换神器替代BeanUtils 了


    博主 默语带您 Go to New World.
    个人主页—— 默语 的博客👦🏻
    《java 面试题大全》
    🍩惟余辈才疏学浅,临摹之作或有不妥之处,还请读者海涵指正。☕🍭
    《MYSQL从入门到精通》数据库是开发者必会基础之一~
    🪁 吾期望此文有资助于尔,即使粗浅难及深广,亦备添少许微薄之助。苟未尽善尽美,敬请批评指正,以资改进。!💻⌨

    在我们日常开发的程序中,为了各层之间解耦,一般会定义不同的对象用来在不同层之间传递数据,比如xxxDTO、xxxVO、xxxQO,当在不同层之间传输数据时,不可避免地经常需要将这些对象进行相互转换。

    今天给大家介绍一个对象转换工具MapStruct,代码简洁安全、性能高,强烈推荐。

    1. MapStruct简介

    MapStruct是一个代码生成器,它基于约定优于配置,极大地简化了Java Bean类型之间映射的实现。特点如下:

    1.基于注解
    2.在编译期自动生成映射转换代码
    3.类型安全、高性能、无依赖性、易于理解阅读

    2.0 MapStruct入门

    2.0.1 简易demo

    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/

    2.1. 引入依赖

    这里使用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>
    
    

    2.2. 需要转换的对象

    创建两个示例对象(EG: 将Demo对象转换为DemoDto对象)
    保证对象之间的值是相同的;

    /**
     * 源对象
     */
    @Data
    public class Demo {
        private Integer id;
        private String name;
    }
    
    /**
     * 目标对象
     */
    @Data
    public class DemoDto {
        private Integer id;
        private String name;
    }
    

    2.3. 创建转换器

    只需要创建一个转换器接口类,并在类上添加 @Mapper 注解即可(官方示例推荐以 xxxMapper 格式命名转换器名称)

    @Mapper
    public interface DemoMapper {
        //使用Mappers工厂获取DemoMapper实现类
        DemoMapper INSTANCE = Mappers.getMapper(DemoMapper.class);
        //定义接口方法,参数为来源对象,返回值为目标对象
        DemoDto toDemoDto(Demo demo);
    }
    

    2.4. 验证

    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)
    

    达到了我们的预期结果。

    2.5. 自动生成的实现类

    为什么声明一个接口就可以转换对象呢?
    我们看一下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方法,易于阅读理解,转换速度非常快。

    3.0 MapStruct进阶

    上面的例子只是小试牛刀,下面开始展示MapStruct的强大之处。

    (限于篇幅,这里不展示自动生成的实现类和验证结果,大家可自行测试)

    场景1:属性名称不同、(基本)类型不同

    • 属性名称不同: 在方法上加上 @Mapping 注解,用来映射属性
    • 属性基本类型不同: 基本类型和String等类型会自动转换

    关键字:@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 库的冰山一角,还有很多强大的功能文中没有提到,感兴趣的朋友可以自行查看官方文档。

    官网地址:https://mapstruct.org/

    4.0 扩展(网上项目参考)

    pom 配置:

    <properties>
            <project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
            <maven.compiler.source>1.8maven.compiler.source>
            <maven.compiler.target>1.8maven.compiler.target>
            <org.mapstruct.version>1.4.1.Finalorg.mapstruct.version>
            <org.projectlombok.version>1.18.12org.projectlombok.version>
    properties>
     
    <dependencies>
            <dependency>
                <groupId>org.mapstructgroupId>
                <artifactId>mapstructartifactId>
                <version>${org.mapstruct.version}version>
            dependency>
     
            
            <dependency>
                <groupId>org.projectlombokgroupId>
                <artifactId>lombokartifactId>
                <version>${org.projectlombok.version}version>
                <scope>providedscope>
            dependency>
     
            
         <dependency>
                <groupId>org.mapstructgroupId>
                <artifactId>mapstruct-processorartifactId>
                <version>${org.mapstruct.version}version>
                <scope>providedscope>
            dependency>
     
    dependencies>
     
    <build>
            <plugins>
                <plugin>
                    <groupId>org.apache.maven.pluginsgroupId>
                    <artifactId>maven-compiler-pluginartifactId>
                    <version>3.8.1version>
                    <configuration>
                        <source>1.8source>
                        <target>1.8target>
                        <annotationProcessorPaths>
                            <path>
                                <groupId>org.projectlombokgroupId>
                                <artifactId>lombokartifactId>
                                <version>${org.projectlombok.version}version>
                            path>
                            <path>
                                <groupId>org.mapstructgroupId>
                                <artifactId>mapstruct-processorartifactId>
                                <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();
        }
     
    }
    

    上面只是最简单的实体映射处理,下面介绍一些高级用法

    1. List 转换

    属性映射基于上面的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);
     
    }
    

    在这里插入图片描述

    4.1 扩展 dozer使用

    <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;
    }
    

    在执行下刚写的转换函数,试试是否能成功转换了

    5.0 本地测试DEMO加深理解

    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
                        **/*.yml</include>
                        <include>**/*.xml
                    
                    false
                
            
    
    
        
    
    
    
    

    代码结构:
    在这里插入图片描述

    先创建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,又能快速完成开发工作了;

    DEMO2:实用版本

    实体类和上面相识;

    在这里插入图片描述
    在这里插入图片描述
    重点 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 {
        /**
        * 意义,目的和功能,以及被用到的地方
    */
    private static final long serialVersionUID = 7474374467497907429L; /** * * PO转VO * @param po po对象 * @param voClass VO的class对象 * @param 具体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 具体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);
    
    
        }
    

    在这里插入图片描述
    想用那种大家随意喽;

    如对本文内容有任何疑问、建议或意见,请联系作者,作者将尽力回复并改进📓;(联系微信:Solitudemind )

  • 相关阅读:
    【k8s】3、kubeadm安装k8s集群
    SAP S/4HANA Public Cloud Enhancement
    Android 协程 异常捕获 异常的传播特性
    【3D建模制作技巧分享】用3dsmax制作炫酷的机器人模型
    计算机毕业设计Java校园代购服务订单管理系统(源码+系统+mysql数据库+Lw文档)
    m3u8文件格式讲解,Python解析m3u8文件,并获取在线地址
    敏捷整洁之道
    修改容器的hosts配置
    短视频运营小技巧,互动引流很重要,内容质量也不能忘
    prometheus + grafana进行服务器资源监控
  • 原文地址:https://blog.csdn.net/qq_42055933/article/details/127115655