Java中MapStruct复制对象的具体使用

 更新时间:2024年12月06日 10:50:33   作者:String、code  
MapStruct是一个用于Java的代码生成器,可以自动生成类型安全的Bean映射代码,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

一、前言

大家在开发中,最让人头疼的就是:对象之间的拷贝,前端的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方法,所以会有问题,官网已经有了解决方案!现在是启动不会报错!

<dependency>
    <groupId>org.mapstruct</groupId>
    <artifactId>mapstruct</artifactId>
    <version>1.5.3.Final</version>
</dependency>
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.24</version>
</dependency>

2. 错误总结

  • 不会自动生成impl实现类?

我们需要加上依赖:

<dependency>
    <groupId>org.mapstruct</groupId>
    <artifactId>mapstruct-processor</artifactId>
    <version>1.5.3.Final</version>
</dependency>
  • 重新启动就会出现和lombok的冲突问题:

java: No property named "name" exists in source parameter(s).
 Type "UserVO" has no properties.

官网解决文章地址

<build>
  <pluginManagement>
      <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>
                          <artifactId>mapstruct-processor</artifactId>
                          <version>1.5.3.Final</version>
                      </path>
                      <path>
                          <groupId>org.projectlombok</groupId>
                          <artifactId>lombok</artifactId>
                          <version>1.18.24</version>
                      </path>
                      <path>
                          <groupId>org.projectlombok</groupId>
                          <artifactId>lombok-mapstruct-binding</artifactId>
                          <version>0.2.0</version>
                      </path>
                  </annotationProcessorPaths>
              </configuration>
          </plugin>
      </plugins>
  </pluginManagement>
</build>

3. 常用实战1

用户表:

@Data
public class User {
    
    private Integer id;
    
    private String username;
    
    private Integer age;
}

前端用户VO:

@Data
public class UserVO {

    private Integer id;

    private String name;

    private Integer age;
}

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

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

}

属性多了可以嵌套:

@Mappings({
        @Mapping(source ="name",target = "username"),
        @Mapping(source ="name1",target = "username1")
})

也可以:

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

编写测试类:

@SpringBootTest
class DemoApplicationTests {

    @Test
    void demoMapstruct(){
        UserVO userVO = new UserVO(1,"小红",18);
        User user = UserMapper.INSTANCE.userVOToUser(userVO);
        System.out.println(user);

    }

}

图片

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

我们看看是怎么实现的:

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

图片

4. 常用实战2

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

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

private BigDecimal score;

新增加一个Score类:

@Data
@AllArgsConstructor
public class Score {

    private Integer studentId;

    private BigDecimal score;
}

调整上面的UserMapper接口:

@Mappings({
       @Mapping(source ="userVO.name",target = "username"),
       @Mapping(source ="score.score",target = "score")
})
User userVOToUser(UserVO userVO, Score score);

测试代码:

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

结果显示正常:

图片

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;

}

看清楚,别导错包了!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"));
    }
}

测试:

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

}

完美转化!

图片

6. 常用实战4

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

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

private String createdAt;

VO里也加上一个:

private LocalDateTime createdAt;

编写个转化类:这里交给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;
    }
}

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

测试一下:

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

完美转化:

图片

五、总结

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

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

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

测试一下:

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

mapstruct开发文档:https://mapstruct.org/documentation/stable/reference/html/#Preface

到此这篇关于Java中MapStruct复制对象的具体使用的文章就介绍到这了,更多相关Java MapStruct复制对象内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 关于springboot-starter-undertow和tomcat的区别说明

    关于springboot-starter-undertow和tomcat的区别说明

    这篇文章主要介绍了关于springboot-starter-undertow和tomcat的区别说明,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-03-03
  • java基础学习笔记之类加载器

    java基础学习笔记之类加载器

    java中的类加载器:java虚拟机中可以安装多个类加载器,系统默认三个主要类加载器,每个类负责加载特定位置的类:BootStrap, ExtClassLoader,AppClassLoader类加载器也是一个java类,因此类加载器也需要被加载,所以必须有第一个类加载器不是java类
    2016-02-02
  • 深入学习Java 动态代理

    深入学习Java 动态代理

    Java 动态代理机制的出现,使得 Java 开发人员不用手工编写代理类,只要简单地指定一组接口及委托类对象,便能动态地获得代理类。下面小编和大家来一起学习一下吧
    2019-05-05
  • Java的动态分派和静态分派的实现

    Java的动态分派和静态分派的实现

    这篇文章主要介绍了Java的动态分派和静态分派的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-03-03
  • Java十分钟精通进阶适配器模式

    Java十分钟精通进阶适配器模式

    适配器模式(Adapter Pattern)是作为两个不兼容的接口之间的桥梁。这种类型的设计模式属于结构型模式,它结合了两个独立接口的功能
    2022-04-04
  • java封装实例用法讲解

    java封装实例用法讲解

    在本篇文章里小编给大家整理了一篇关于java封装实例用法及相关知识点,有兴趣的朋友们可以参考下。
    2021-01-01
  • SpringBoot公共页面抽取方法实现过程介绍

    SpringBoot公共页面抽取方法实现过程介绍

    这篇文章主要介绍了SpringBoot抽取公共页面的方法实现过程,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习吧
    2022-10-10
  • Java stringBuilder的使用方法及实例解析

    Java stringBuilder的使用方法及实例解析

    这篇文章主要介绍了Java stringBuilder的使用方法及实例解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2019-09-09
  • Java线程方法之从线程角色到实战避坑指南

    Java线程方法之从线程角色到实战避坑指南

    Java语言支持多线程编程,它允许在同一个程序中同时运行多个线程来执行不同的任务,这有助于提高程序的执行效率和响应速度,这篇文章主要介绍了Java线程方法之从线程角色到实战避坑指南的相关资料,需要的朋友可以参考下
    2025-12-12
  • 教你如何正确了解java三大特性!!!!

    教你如何正确了解java三大特性!!!!

    所有的面向对象编程语言的思路都是差不多的,而这三大特性,则是思路中的支柱点,接下来我就重点讲解了一下java三大特性,感兴趣的朋友跟随脚本之家小编一起看看吧
    2021-07-07

最新评论