ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

初识org.mapstruct:mapstruct

2022-03-22 00:02:14  阅读:275  来源: 互联网

标签:mapstruct Mapping 初识 import org UserDto public


文章目录

前言

最近发现了个很好玩的工具包org.mapstruct,里面处理类型转换的功能总觉得很高大上。特此写一篇博客记录测试、使用心得。

简介

平时的开发中,针对与数据库做数据交互操作时,一般定义一个vo或者pojo类,在接收前端页面的参数信息时,会采取定义一个dto类的形式。

但是在开发中,难免会碰见需要将 dto 类转换为对应的 vo 类,达到和数据库数据交互的目的。

以前,我最喜欢采取自己手写get/set的代码,手动将数据信息进行转换。稍微用得高大上点的就是采取BeanUtils.copyProperties(source,target)

但机缘巧合下,发现了更好用的工具类org.mapstruct:mapstruct

他能够让类型转换之间更简单快捷,其次还支持部分参数调用指定Java代码解析!

接下来就一起见证下他的神奇之处。

依赖导入

本次测试环境采取Springboot 2.1.4.RELEASEmapstruct-processor 1.3.0.Final

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.1.4.RELEASE</version>
    <relativePath /> <!-- lookup parent from repository -->
</parent>
<dependencies>
	<dependency>
    	<groupId>org.mapstruct</groupId>
        <artifactId>mapstruct-jdk8</artifactId>
        <version>1.3.0.Final</version>
    </dependency>
    <dependency>
        <groupId>org.mapstruct</groupId>
        <artifactId>mapstruct-processor</artifactId>
        <version>1.3.0.Final</version>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.16.20</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>

常见的几种处理方式

原始方式 get/set

既然文章开头说到pojodto两种类型的转换操作,接下来就新建这两个类。
User.java

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
    private Long id;
    private String name;
    private Integer age;
    private String email;
}

UserDto.java

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class UserDto {
    private Long id;
    private String name;
    private Integer age;
    private String email;
}

一般的类型转换,参考下列测试类:

import cn.xj.StartApplication;
import cn.xj.pojo.User;
import cn.xj.pojo.UserDto;
import lombok.extern.slf4j.Slf4j;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

@RunWith(SpringRunner.class)
@SpringBootTest(classes = StartApplication.class)
@Slf4j
public class Test1 {
    UserDto userDto = null;
    @Before
    public void before(){
        // 假定这是一个前端传递来得数据信息
        userDto = new UserDto(1L,"xiangjiao dto",22,null);
    }

    @Test
    public void test1(){
        User user = new User();
        user.setId(userDto.getId());
        user.setName(userDto.getName());
        user.setAge(userDto.getAge());
        user.setEmail(userDto.getEmail());

        System.out.println(user);
    }
}

采取一般的get/set方式,手动将指定的类中的数据转换至指定的类中

BeanUtils.copyProperties 实现

BeanUtils.copyProperties也能实现类似上面的功能,如下所示:

UserDto userDto = null;
@Before
public void before(){
    // 假定这是一个前端传递来得数据信息
    userDto = new UserDto(1L,"xiangjiao dto",22,null);
}
@Test
public void test2(){
    User user = new User();
    // 第一个参数表示:源数据对象
    // 第二个参数表示:目标对象
    BeanUtils.copyProperties(userDto,user);

    System.out.println(user);
}

主角 mapstruct 登场

1、简单使用

mapstruct如果需要实现上面的功能,需要编写一个接口,如下所示:

package cn.xj.interfaces;

import cn.xj.pojo.User;
import cn.xj.pojo.UserDto;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.Mappings;
import org.mapstruct.factory.Mappers;

@Mapper // 和 Mybatis的很像哦
public interface MapStructInf {
    /**
     * 获取该类自动生成的实现类的实例
     * 接口中的属性都是 public static final 的 方法都是public abstract的
     */
    MapStructInf instances = Mappers.getMapper(MapStructInf.class);

    /**
     * 这个方法就是用于实现对象属性复制的方法
     *
     * @Mapping 用来定义属性复制规则 source 指定源对象属性 target指定目标对象属性
     *
     * @param userDto 这个参数就是 源对象,也就是需要 被复制 的对象
     * @return 返回的是 目标对象,就是最终的结果对象
     */
    @Mappings({
            @Mapping(source = "id",target = "id"),
            @Mapping(source = "name",target = "name"),
            @Mapping(source = "age",target = "age"),
            @Mapping(source = "email",target = "email")
    })
    User tranceToUser(UserDto userDto);
}

编写一个测试类,进行测试:

import cn.xj.StartApplication;
import cn.xj.interfaces.MapStructInf;
import cn.xj.pojo.User;
import cn.xj.pojo.UserDto;
import lombok.extern.slf4j.Slf4j;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

@RunWith(SpringRunner.class)
@SpringBootTest(classes = StartApplication.class)
@Slf4j
public class MapstructTest {

    UserDto userDto = null;
    @Before
    public void before(){
        // 假定这是一个前端传递来得数据信息
        userDto = new UserDto(1L,"xiangjiao dto",22,null);
    }

    @Test
    public void test1(){
        User user = MapStructInf.instances.tranceToUser(userDto);
        System.out.println(user);
    }
}

在这里插入图片描述
初步一看,有些观众大姥爷可能会说:

写这么一大篇的接口,就为了转换一个类,吃多了吧!

上面只是简单的使用测试,接下来看点有意思的。

2、转换集合类

继续在指定的cn.xj.interfaces.MapStructInf接口类中,定义一个转换集合的方法。如下所示:

import cn.xj.pojo.User;
import cn.xj.pojo.UserDto;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.Mappings;
import org.mapstruct.factory.Mappers;

import java.util.List;

@Mapper // 和 Mybatis的很像哦
public interface MapStructInf {
    /**
     * 获取该类自动生成的实现类的实例
     * 接口中的属性都是 public static final 的 方法都是public abstract的
     */
    MapStructInf instances = Mappers.getMapper(MapStructInf.class);

    /**
     * 这个方法就是用于实现对象属性复制的方法
     *
     * @Mapping 用来定义属性复制规则 source 指定源对象属性 target指定目标对象属性
     *
     * @param userDto 这个参数就是 源对象,也就是需要 被复制 的对象
     * @return 返回的是 目标对象,就是最终的结果对象
     */
//    @Mappings({
//            @Mapping(source = "id",target = "ids"),
//            @Mapping(source = "name",target = "names"),
//            @Mapping(source = "age",target = "ages"),
//            @Mapping(source = "email",target = "emails")
//    })
//    User tranceToUser(UserDto userDto);

    // 转换集合
    List<User> tranceToUserList(List<UserDto> userDtoList);
}

编写测试类进行测试:

import cn.xj.StartApplication;
import cn.xj.interfaces.MapStructInf;
import cn.xj.pojo.User;
import cn.xj.pojo.UserDto;
import lombok.extern.slf4j.Slf4j;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

import java.util.Arrays;
import java.util.List;

@RunWith(SpringRunner.class)
@SpringBootTest(classes = StartApplication.class)
@Slf4j
public class MapstructTest {

    List<UserDto> userDtoList;
    @Before
    public void before(){
        // 假定这是一个前端传递来得数据信息

        userDtoList = Arrays.asList(
                new UserDto(1L,"xiangjiao dto",11,null),
                new UserDto(2L,"xiangjiao dto",22,null),
                new UserDto(3L,"xiangjiao dto",33,null),
                new UserDto(4L,"xiangjiao dto",44,null));
    }

    @Test
    public void test2(){
        List<User> users = MapStructInf.instances.tranceToUserList(userDtoList);
        users.forEach(e->{
            System.out.println(e);
        });
    }
}

结果却是可行的。
在这里插入图片描述

【注意:】这里有个小细节!

如果两个类的属性名是一样的,可以执行成功!
如果两个类的属性名 不同,则数据转换失败!!

为了测试这个问题,则需要将User.java类中的属性名和UserDto.java类中的属性名区分开。如下所示:

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
    private Long ids;
    private String names;
    private Integer ages;
    private String emails;
}

再次指定上面的test2测试方法,控制台打印日志信息如下所示:
在这里插入图片描述
【疑问:】当这种问题出现,如何解决呢?

只需要增加一个配置方法!

import cn.xj.pojo.User;
import cn.xj.pojo.UserDto;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.Mappings;
import org.mapstruct.factory.Mappers;

import java.util.List;

@Mapper // 和 Mybatis的很像哦
public interface MapStructInf {
    /**
     * 获取该类自动生成的实现类的实例
     * 接口中的属性都是 public static final 的 方法都是public abstract的
     */
    MapStructInf instances = Mappers.getMapper(MapStructInf.class);

    /**
     * 这个方法就是用于实现对象属性复制的方法
     *
     * @Mapping 用来定义属性复制规则 source 指定源对象属性 target指定目标对象属性
     *
     * @param userDto 这个参数就是 源对象,也就是需要 被复制 的对象
     * @return 返回的是 目标对象,就是最终的结果对象
     */
    @Mappings({
            @Mapping(source = "id",target = "ids"),
            @Mapping(source = "name",target = "names"),
            @Mapping(source = "age",target = "ages"),
            @Mapping(source = "email",target = "emails")
    })
    User tranceToUser(UserDto userDto);

    // 转换集合
    List<User> tranceToUserList(List<UserDto> userDtoList);
}

再次执行test2(),可以看到控制台打印日志如下所示:
在这里插入图片描述
【原因:】

两个类之间属性别名不一致时,可以采取定义单个类转换的关系,实现集合数据的转换!

3、进阶技能 expression

除了上面的基本操作之外,mapstruct还能支持转换时,采取Java代码方式转换。

比如:

定义一个性别枚举类,但是User.java类接收的是性别名称,UserDto.java类中却传递的是性别编号

首先,定义一个枚举类,提供可以根据编号查询名称的函数

package cn.xj.pojo2;

public enum SexEnum {

    man(1,"男"),
    woman(2,"女");

    private Integer value;
    private String name;
	
	// 注意这里一定要是 static
	// expression 只能调用静态方法
    public static String getValByName(Integer value){
        String names = null;
        for (SexEnum sexEnum : values()){
            Integer value1 = sexEnum.getValue();
            if(value1.equals(value)){
                names = sexEnum.getName();
                break;
            }
        }
        return names;
    }

    SexEnum(Integer value, String name) {
        this.value = value;
        this.name = name;
    }

    public Integer getValue() {
        return value;
    }

    public void setValue(Integer value) {
        this.value = value;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

再修改对应的User.java类:

保证User.java类接收性别别名

package cn.xj.pojo2;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
    private Long ids;
    private String names;
    private Integer ages;
    private String emails;

    private String sex; // 性别别名
}

定义前端页面数据接收类UserDto.java

保证性别信息采取编号接收

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class UserDto {
    private Long id;
    private String name;
    private Integer age;
    private String email;

    private Integer sexNum; // 性别编号
}

定义转换方式:

import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.Mappings;
import org.mapstruct.factory.Mappers;

@Mapper // 和 Mybatis的很像哦
public interface MapStructInf2 {
    /**
     * 获取该类自动生成的实现类的实例
     * 接口中的属性都是 public static final 的 方法都是public abstract的
     */
    MapStructInf2 instances = Mappers.getMapper(MapStructInf2.class);

    /**
     * 这个方法就是用于实现对象属性复制的方法
     *
     * @Mapping 用来定义属性复制规则 source 指定源对象属性 target指定目标对象属性
     *
     * @param userDto 这个参数就是 源对象,也就是需要 被复制 的对象
     * @return 返回的是 目标对象,就是最终的结果对象
     */
    @Mappings({
            @Mapping(source = "id",target = "ids"),
            @Mapping(source = "name",target = "names"),
            @Mapping(source = "age",target = "ages"),
            @Mapping(source = "email",target = "emails"),
            @Mapping(target = "sex",expression = "java(cn.xj.pojo2.SexEnum.getValByName(userDto.getSexNum()))")
    })
    User tranceToUser(UserDto userDto);
}

执行后的结果如下所示:
在这里插入图片描述

参考资料

org.mapstruct:mapstruct 包的使用

mapstruct 实体转换及List转换

代码下载

gitee 仓库地址

标签:mapstruct,Mapping,初识,import,org,UserDto,public
来源: https://blog.csdn.net/qq_38322527/article/details/123647995

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有