• MapStruct复制对象详细介绍


    MapStruct复制对象详细介绍

    一、前言

    大家在开发中,最让人头疼的就是:对象之间的拷贝,前端的VO和数据库的Entity不一致!

    性能最好的就是手动set,主要是枯燥且无技术含量,不仅耗费大量时间而且很容易出错;

    所以我们要成为优秀的程序员,要多借助轮子,开发效率事半功倍,开发技能也是增长不少!

    如果系统性能没有要求,怎么实现都是好的,但是我们要有追求哈,追求高质量!

    每个东西都有存在的价值,不要捧一踩一哈!

    二、MapStruct简介

    MapStruct是基于JSR 269的Java注释处理器,用于生成类型安全的 Bean 映射类。

    您所要做的就是定义一个映射器接口,该接口声明任何所需的映射方法。在编译过程中,MapStruct将生成此接口的实现。此实现使用纯 Java 方法调用在源对象和目标对象之间进行映射,即无反射或类似内容。

    与手动编写映射代码相比,MapStruct通过生成繁琐且容易出错的代码来节省时间。遵循配置方法的约定,MapStruct使用合理的默认值,但在配置或实现特殊行为时会步入歧途。

    图片在这里插入图片描述

    三、优势

    与动态映射框架相比,MapStruct具有以下优点:

    • 通过使用普通方法调用而不是反射快速执行

    • 编译时类型安全:只能映射彼此映射的对象和属性,不会意外地将订单实体映射到客户 DTO 等。

    • 在构建时清除错误报告,如果

      • 映射不完整(并非所有目标属性都已映射)
      • 映射不正确(找不到正确的映射方法或类型转换)

    性能图大家可以看一下:图片

    四、整合实战

    0. 使用

    @Mapper将接口标记为映射接口 对于源对象和目标对象中具有不同名称的属性,可以使用注释来配置名称:@Mapping按照约定,接口声明一个成员Mappers INSTANCE,为客户端提供对映射器实现的访问。下面我们来具体使用!

    1. 导入依赖

    这里使用最新的,如果引入了lombok可能会有问题,就是他们俩都是在编译期运行的,mapstruct如果比lombok先执行,就会找不到get、set方法,所以会有问题,官网已经有了解决方案!现在是启动不会报错!

    
        org.mapstruct
        mapstruct
        1.5.3.Final
    
    
        org.projectlombok
        lombok
        1.18.24
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    2. 错误总结

    1. 不会自动生成impl实现类?

    我们需要加上依赖:

    
        org.mapstruct
        mapstruct-processor
        1.5.3.Final
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    1. 重新启动就会出现和lombok的冲突问题:
    java: No property named "name" exists in source parameter(s).
     Type "UserVO" has no properties.
    
    • 1
    • 2

    官网解决文章地址

    
      
          
              
                  org.apache.maven.plugins
                  maven-compiler-plugin
                  3.8.1
                  
                      1.8
                      1.8
                      
                          
                              org.mapstruct
                              mapstruct-processor
                              1.5.3.Final
                          
                          
                              org.projectlombok
                              lombok
                              1.18.24
                          
                          
                              org.projectlombok
                              lombok-mapstruct-binding
                              0.2.0
                          
                      
                  
              
          
      
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    3. 常用实战1

    用户表:

    @Data
    public class User {
        
        private Integer id;
        
        private String username;
        
        private Integer age;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    前端用户VO:

    @Data
    public class UserVO {
    
        private Integer id;
    
        private String name;
    
        private Integer age;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    我们创建接口进行两个对象之间的映射:

    import com.example.demo.mapstruct.entity.User;
    import com.example.demo.mapstruct.entity.UserVO;
    import org.mapstruct.Mapper;
    import org.mapstruct.Mapping;
    import org.mapstruct.factory.Mappers;
    
    /**
     * @author wangzhenjun
     * @date 2023/1/28 16:05
     */
    @Mapper
    public interface UserMapper {
    
        UserMapper INSTANCE = Mappers.getMapper(UserMapper.class);
    
        @Mapping(source ="name",target = "username")
        User userVOToUser(UserVO userVO);
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    属性多了可以嵌套:

    @Mappings({
            @Mapping(source ="name",target = "username"),
            @Mapping(source ="name1",target = "username1")
    })
    
    • 1
    • 2
    • 3
    • 4

    也可以:

    @Mapping(source ="name",target = "username")
    @Mapping(source ="name1",target = "username1")
    
    • 1
    • 2

    编写测试类:

    @SpringBootTest
    class DemoApplicationTests {
    
        @Test
        void demoMapstruct(){
            UserVO userVO = new UserVO(1,"小红",18);
            User user = UserMapper.INSTANCE.userVOToUser(userVO);
            System.out.println(user);
    
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    图片我们看到拷贝没有任何问题!

    我们看看是怎么实现的:

    mapstruct会在编译期自动生成实现类去帮助我们去赋值,不指定默认策略,名称一致进行copy!不一致可以按上面的进行指定,不指定则不会有set方法!

    图片在这里插入图片描述

    4. 常用实战2

    下面进行多个源参数的映射方法演示:

    我们把user类加上一个字段:

    private BigDecimal score;
    
    • 1

    新增加一个Score类:

    @Data
    @AllArgsConstructor
    public class Score {
    
        private Integer studentId;
    
        private BigDecimal score;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    调整上面的UserMapper接口:

    @Mappings({
           @Mapping(source ="userVO.name",target = "username"),
           @Mapping(source ="score.score",target = "score")
    })
    User userVOToUser(UserVO userVO, Score score);
    
    • 1
    • 2
    • 3
    • 4
    • 5

    测试代码:

    UserVO userVO = new UserVO(1,"小红",18);
    Score score = new Score(1,new BigDecimal(100));
    User user = UserMapper.INSTANCE.userVOToUser(userVO,score);
    System.out.println(user);
    
    • 1
    • 2
    • 3
    • 4

    结果显示正常:

    图片在这里插入图片描述

    5. 常用实战3

    我们再来看一个企业级能够用得上的,就是自定义方法,然后进行赋值:

    场景:一个商品有长宽高,我们把长宽高从cm变为m!

    还有很多String转Integer、Float等等,都是按照下面这种自定义方法去实现!

    VO和对象都是一样的哈!

    @Data
    @AllArgsConstructor
    public class ProductVO {
    
        private Integer id;
    
        private String name;
    
        private BigDecimal length;
    
        private BigDecimal width;
    
        private BigDecimal high;
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    看清楚,别导错包了!qualifiedByName:指定自定义方法的名称@Named("cmToM"):起别名,不使用找不到方法

    可以写一起,也可以整一个工具类里写方法,在这里进行引用!如果是使用spring,我们可以把接口作为bean进行注入调用(推荐) 加上参数即可开启:@Mapper(componentModel = MappingConstants.ComponentModel.SPRING)

    /**
     * @author wangzhenjun
     * @date 2023/1/28 17:13
     */
    @Mapper(componentModel = MappingConstants.ComponentModel.SPRING)
    public interface ProductMapper {
    
        @Mapping(source = "length",target = "length",qualifiedByName = "cmToM")
        @Mapping(source = "width",target = "width",qualifiedByName = "cmToM")
        @Mapping(source = "high",target = "high",qualifiedByName = "cmToM")
        Product productVOToPrduct(ProductVO productVO);
    
        @Named("cmToM")
        default BigDecimal cmToM (BigDecimal oldValue){
            if (oldValue == null) {
                return BigDecimal.ZERO;
            }
            return oldValue.divide(new BigDecimal("100"));
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    测试:

    @SpringBootTest
    class DemoApplicationTests {
    
        @Autowired
        private ProductMapper productMapper;
    
        @Test
        void demoMapstruct(){
    
            ProductVO productVO = new ProductVO(1,"美丽家园地板",new BigDecimal(100),new BigDecimal(50),new BigDecimal(8));
            Product product = productMapper.productVOToProduct(productVO);
    
            System.out.println(product);
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    完美转化!

    图片在这里插入图片描述

    6. 常用实战4

    在实战一个LocalDateTime、String相互转化的,后面大家可以去官网文档去找你需要的:

    在刚刚的商品类加个字段:

    private String createdAt;
    
    • 1

    VO里也加上一个:

    private LocalDateTime createdAt;
    
    • 1

    编写个转化类:这里交给spring管理了,因为ProductMapper也交给spring管理,不加的话会找不到此类!

    @Component
    public class LocalDateTimeMapper {
    
        public String asString(LocalDateTime date) {
            DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
            return date != null ? date.format(formatter): null;
        }
    
        public LocalDateTime asLocalDateTime(String date) {
            DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
            return date != null ? LocalDateTime.parse(date,formatter) : null;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    ProductMapper修改一下:

    uses = LocalDateTimeMapper.class使用咱们上面写的类即可!

    @Mapper(componentModel = MappingConstants.ComponentModel.SPRING,uses = LocalDateTimeMapper.class)
    public interface ProductMapper {
    
        @Mapping(source = "length",target = "length",qualifiedByName = "cmToM")
        @Mapping(source = "width",target = "width",qualifiedByName = "cmToM")
        @Mapping(source = "high",target = "high",qualifiedByName = "cmToM")
        Product productVOToProduct(ProductVO productVO);
    
        @Named("cmToM")
        default BigDecimal cmToM (BigDecimal oldValue){
            if (oldValue == null) {
                return BigDecimal.ZERO;
            }
            return oldValue.divide(new BigDecimal("100"));
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    测试一下:

    ProductVO productVO = new ProductVO(1,"美丽家园地板",
    new BigDecimal(100),new BigDecimal(50),
    new BigDecimal(8), LocalDateTime.now());
    Product product = productMapper.productVOToProduct(productVO);
    System.out.println(product);
    
    • 1
    • 2
    • 3
    • 4
    • 5

    完美转化:

    图片在这里插入图片描述

    六、总结

    通过简介到实战,这时咱们就是优雅的程序员了!

    更多的例子可以去官网进行查看:

    alue.divide(new BigDecimal(“100”));
    }
    }

    
    测试一下:
    
    
    • 1
    • 2
    • 3

    ProductVO productVO = new ProductVO(1,“美丽家园地板”,
    new BigDecimal(100),new BigDecimal(50),
    new BigDecimal(8), LocalDateTime.now());
    Product product = productMapper.productVOToProduct(productVO);
    System.out.println(product);

    
    完美转化:
    
    [外链图片转存中...(img-CHac062B-1696815679059)]在这里插入图片描述
    
    ## 六、总结
    
    通过简介到实战,这时咱们就是优雅的程序员了!
    
    更多的例子可以去官网进行查看:
    
    mapstruct开发文档:https://mapstruct.org/documentation/stable/reference/html/#Preface
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
  • 相关阅读:
    小程序版本审核未通过,需在开发者后台「版本管理—提交审核——小程序订单中心path」设置订单中心页path,请设置后再提交代码审核
    20221206英语学习
    Python浪漫星空
    图论基础知识 深度优先(Depth First Search, 简称DFS),广度优先(Breathe First Search, 简称DFS)
    第17章 站点构建
    4. 串的【朴素模式匹配算法】、【KPM算法:求next数组、nextval数组】
    【论文写作】符号:矩阵、向量的乘法、内积、点积等
    4、模板(二叉树,红黑树,STL的实现)
    基于java学生考勤管理系统设计——计算机毕业设计
    常规加密算法是什么?原理是怎么样?有哪些?
  • 原文地址:https://blog.csdn.net/Lxl1418/article/details/133694676