我正在参加「启航方案」

一、前言

我们在开发中,最让人头疼的便是:方针之间的复制,前端的VO和数据库的Entity不共同!

功能最好的便是手动set,主要是单调且无技术含量,不只耗费很多时间并且很容易出错;

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

假如体系功能没有要求,怎么完成都是好的,可是我们要有追求哈,追求高质量!

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

二、MapStruct简介

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

您所要做的便是界说一个映射器接口,该接口声明任何所需的映射办法。在编译过程中,MapStruct将生成此接口的完成。此完成运用纯 Java 办法调用在源方针和方针方针之间进行映射,即无反射或相似内容。

与手动编写映射代码比较,MapStruct经过生成繁琐且容易出错的代码来节省时间。遵从配置办法的约定,MapStruct运用合理的默许值,但在配置或完成特别行为时会步入歧途。

高效、优雅的对象copy之MapStruct入门到精通,实战踩坑版

三、优势

与动态映射结构比较,MapStruct具有以下长处:

  • 经过运用普通办法调用而不是反射快速履行

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

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

    • 映射不完整(并非一切方针特点都已映射)

    • 映射不正确(找不到正确的映射办法或类型转换)

功能图我们能够看一下:

高效、优雅的对象copy之MapStruct入门到精通,实战踩坑版

四、整合实战

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. 错误总结

  1. 不会主动生成impl完成类?

我们需要加上依靠:

<dependency>
    <groupId>org.mapstruct</groupId>
    <artifactId>mapstruct-processor</artifactId>
    <version>1.5.3.Final</version>
</dependency>
  1. 重新发动就会呈现和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);
    }
}

高效、优雅的对象copy之MapStruct入门到精通,实战踩坑版
我们看到复制没有任何问题!

我们看看是怎么完成的:

mapstruct会在编译期主动生成完成类去协助我们去赋值,不指定位默许策略,称号共同进行copy! 不共同能够按上面的进行指定,不指定则不会有set办法!

高效、优雅的对象copy之MapStruct入门到精通,实战踩坑版

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

成果显现正常:

高效、优雅的对象copy之MapStruct入门到精通,实战踩坑版

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

完美转化!

高效、优雅的对象copy之MapStruct入门到精通,实战踩坑版

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

完美转化:

高效、优雅的对象copy之MapStruct入门到精通,实战踩坑版

六、总结

经过简介到实战,这时我们便是高雅的程序员了!

更多的比如能够去官网进行查看:

mapstruct开发文档

写作不易,我们给点支撑,你的支撑是我写作的动力哈!

对你有协助,还请不要吝啬你的发财小手点点重视哈!

重视小编的微信大众号:『小王博客基地』,一起交流学习!文章首发看哦!