MapStruct_概念、如何使用、子集和映射、合并、Spring方式、表达式、自定义切面处理

news2024/10/6 8:24:40

文章目录

  • ①. 什么是MapStruct?
  • ②. 如何使用MapStruct?
  • ③. 子集和映射
  • ④. 合并映射
  • ⑤. Spring依赖注入
  • ⑥. 常量、默认值和表达式
  • ⑦. 自定义切面处理

①. 什么是MapStruct?

  • ①. MapStruct是一款基于Java注解的对象属性映射工具,使用的时候我们只要在接口中定义好对象属性映射规则,它就能自动生成映射实现类,不使用反射,性能优秀,能实现各种复杂映射

  • ②. 在平时CRUD的工作中,经常需要做PO、VO、DTO之间的转换。简单的对象转换,使用BeanUtils基本上是够了,但是复杂的转换,如果使用它的话又得写一堆Getter、Setter方法了。BeanUtils 就是一个大老粗,只能同属性映射,或者在属性相同的情况下,允许被映射的对象属性少;但当遇到被映射的属性数据类型被修改或者被映射的字段名被修改,则会导致映射失败

  • ③. IDEA下载mapstruct support插件
    在这里插入图片描述

②. 如何使用MapStruct?

  • ①. 引入MapStruct依赖
    <properties>
        <lombok.version>1.18.12</lombok.version>
        <mapstruct.version>1.4.2.Final</mapstruct.version>
    </properties>
	<!--MapStruct相关依赖-->
	<dependency>
		<groupId>org.mapstruct</groupId>
		<artifactId>mapstruct</artifactId>
		<version>${mapstruct.version}</version>
	</dependency>
	<dependency>
		<groupId>org.mapstruct</groupId>
		<artifactId>mapstruct-processor</artifactId>
		<version>${mapstruct.version}</version>
		<scope>compile</scope>
	</dependency>
		
    <!--需要加上下面这个插件,不然会报错ClassNotFoundException-->
    <build>
        <plugins>
            <!-- MapStruct 编译器插件 -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                    <annotationProcessorPaths>
                        <path>
                            <groupId>org.mapstruct</groupId>
                            <artifactId>mapstruct-processor</artifactId>
                            <version>1.4.2.Final</version>
                        </path>
                    </annotationProcessorPaths>
                </configuration>
            </plugin>
        </plugins>
    </build>
  • ②. 创建我们所需要的案例实体类
@Data
@NoArgsConstructor
@AllArgsConstructor
@Accessors(chain = true)
public class User {
    private Integer id ;//用户id
    private String userName;//用户名
    private String password; //密码
    private Date birthday;//生日
    private String tel;//电话号码
    private String email; //邮箱
    private String idCardNo;//身份证号
    private String icon; //头像
    private Integer gender;//性别
}
@Data
@NoArgsConstructor
@AllArgsConstructor
@Accessors(chain = true)
@EqualsAndHashCode(callSuper = false)
public class UserVo {
    private Long id ;//用户id
    private String userName;//用户名
    private String password; //密码
	// 与User对象不同的类型
    private String birthday;//生日
    //与User不同的名称
    private String telNumber;//电话号码
    private String email; //邮箱
    private String idCardNo;//身份证号
    private String icon; //头像
    private Integer gender;//性别
}
  • ③. 创建映射接口(目的:实现同名同类型属性、不同名称属性、不同类型属性的映射)
/**
 * unmappedTargetPolicy:
 * 目标属性不存在时的处理策略,可选值有:IGNORE默认值、WARN和ERROR。
 * IGNORE默认值:忽略未映射的源属性
 * WARN:任何未映射的源属性都将在生成时引起警告,基于javax.tools.Diagnostic.Kind.WARNING的警告。
 * ERROR:任何未映射的源属性都将导致映射代码生成失败。
 *
 */
@Mapper(unmappedTargetPolicy = ReportingPolicy.IGNORE)
public interface UserMapper {
    UserMapper INSTANCE = Mappers.getMapper(UserMapper.class);

    @Mapping(source = "tel",target = "telNumber")
    @Mapping(source = "birthday",target = "birthday",dateFormat = "yyyy-MM-dd")
    UserVo convertToVo(User user);
}
  • ④. 案例演示
@RestController
@RequestMapping("/testController")
@Slf4j
public class TestController {

    @GetMapping("/mapStructToVo")
    public String mapStructToVo() {
        User user = new User();
        user.setId(1).setEmail("84519548@qq.com").setUserName("tang").setBirthday(new Date()).setTel("18774149799");
        UserVo userVo = UserMapper.INSTANCE.convertToVo(user);
        // {"birthday":"2023-10-07","email":"84519548@qq.com","id":1,"telNumber":"18774149799","userName":"tang"}
        System.out.println(JSON.toJSONString(userVo));
        return JSON.toJSONString(userVo);
    }
}

③. 子集和映射

  • ①. MapStruct对于对象中包含子对象也需要转换的情况也是有所支持的

  • ②. 有一个订单PO对象Order,嵌套有User和Product对象

@Data
@EqualsAndHashCode(callSuper = false)
public class Order {
    private Long id;
    private String orderNo;//订单号
    private Date createTime;
    private String receiverAddress; //收货地址
    private User user;//订单所属的用户
    private List<Product> productList; //商品集合
}
@Data
@Accessors(chain = true)
@EqualsAndHashCode(callSuper = false)
public class Product {
    private Long id;
    private String productSn;
    private String name;
    private String subTitle;
    private String brandName;
    private BigDecimal price;
    private Integer count;//商品数量
    private Date createTime;
}
  • ③. 我们需要转换为OrderDo对象,OrderDo中包含UserVo和ProductVo两个子对象同样需要转换;
@Data
@EqualsAndHashCode(callSuper = false)
public class OrderVo {
    private Long id;
    private String orderNo; //订单号
    private Date createTime;
    private String receiverAddress; //收货地址
    //子对象映射Dto
    private UserVo userVo;//订单所属的用户
    //子对象数组映射Dto
    private List<ProductVo> productVoList; //商品集合
}
@Data
@EqualsAndHashCode(callSuper = false)
public class ProductVo {
    //使用常量
    private Long id;
    //使用表达式生成属性
    private String productSn;
    private String name;
    private String subTitle;
    private String brandName;
    private BigDecimal price;
    //使用默认值
    private Integer number;//商品数量
    private Date createTime;
}
  • ④. 使用uses将子对象的转换Mapper注入进来,然后通过@Mapping设置好属性映射规则即可
@Mapper(uses = {UserMapper.class,ProductMapper.class})
public interface OrderMapper {
    OrderMapper INSTANCE = Mappers.getMapper(OrderMapper.class);

    @Mapping(source = "user",target = "UserVo")
    @Mapping(source = "productList",target = "productVoList")
    OrderVo convertToVo(Order order);
}
@Mapper(imports = {UUID.class})
public interface ProductMapper {
    ProductMapper INSTANCE = Mappers.getMapper(ProductMapper.class);

    @Mapping(target = "id",constant = "-1L")
    @Mapping(source = "count",target = "number",defaultValue = "1")
    @Mapping(target = "productSn",expression = "java(UUID.randomUUID().toString())")
    ProductVo convertToVo(Product product);
}
  • ⑤. 直接通过Mapper中的INSTANCE实例调用转换方法toDto;
    @GetMapping("/mapStructToSubVo")
    public String  mapStructToSubVo() {
        //创建一个user对象
        User user = new User();
        user.setId(1).setEmail("845195485@qq.com").setUserName("tang")
                .setBirthday(new Date()).setTel("18774149799");
        //创建productList
        List<Product> productList = new ArrayList<>();
        productList.add(new Product().setCount(3).setName("test-nameA"));
        productList.add(new Product().setCount(7).setName("test-nameB"));
        Order order = new Order();
        order.setUser(user).setProductList(productList);
        OrderVo orderVo = OrderMapper.INSTANCE.convertToVo(order);
        // {"productVoList":[{"id":-1,"name":"test-nameA","number":3,"productSn":"d7cacdd0-4a13-46b1-a76b-fba7607d68ea"},{"id":-1,"name":"test-nameB","number":7,"productSn":"18f7c91e-c5f1-4bb6-8ae3-6e1e5847f03c"}],"userVo":{"birthday":"2023-10-12","email":"845195485@qq.com","id":1,"telNumber":"18774149799","userName":"tang"}}
        System.out.println(JSON.toJSONString(orderVo));
        return JSON.toJSONString(orderVo);
    }

④. 合并映射

  • ①. MapStruct支持把多个对象属性映射到一个对象中去

  • ②. 把User和Order的部分属性映射到UserOrderDto中去

@Data
public class UserOrderVo {
    private Long id ;//用户id
    private String userName;//用户名
    private String password; //密码
    //与PO类型不同的属性
    private String birthday;//生日
    //与PO名称不同的属性
    private String telNumber;//电话号码
    private String email;
    private String idCardNo;//身份证号
    private String icon; //头像
    private Integer gender;//性别
    private String orderNo; //订单号
    private String receiverAddress; //用户收货地址
}
  • ③. 在Mapper中添加toUserOrderVo方法,这里需要注意的是由于参数中具有两个属性,需要通过参数名称.属性的名称来指定source来防止冲突这两个参数中都有id属性
@Mapper
public interface UserMapper {
    UserMapper INSTANCE = Mappers.getMapper(UserMapper.class);

    @Mapping(source = "user.tel",target = "telNumber")
    @Mapping(source = "user.birthday",target = "birthday",dateFormat = "yyyy-MM-dd")
    @Mapping(source = "user.id",target = "id")
    @Mapping(source = "order.orderNo", target = "orderNo")
    @Mapping(source = "order.receiverAddress", target = "receiverAddress")
    UserOrderVo toUserOrderVo(User user, Order order); 
}
  • ④. 测试
    @ApiOperation(value = "组合映射")
    @GetMapping("/compositeMapping")
    public String compositeMapping() {
        //新建一个user对象
        User user = new User();
        user.setBirthday(new Date()).setTel("110");
        //新建一个Order对象
        Order order = new Order();
        order.setReceiverAddress("湖南长沙测试").setOrderNo("123456789");
        // {"birthday":"2023-10-12","orderNo":"123456789","receiverAddress":"湖南长沙测试","telNumber":"110"}
        UserOrderVo userOrderVo = UserMapper.INSTANCE.toUserOrderVo(user,order);
        System.out.println(JSON.toJSONString(userOrderVo));
        return JSON.toJSONString(userOrderVo);
    }

⑤. Spring依赖注入

  • ①. 想要使用依赖注入,我们只要将@Mapper注解的componentModel参数设置为spring即可,这样在生成接口实现类时,MapperStruct会为其添加@Component注解
@Mapper(componentModel = "spring")
public interface UserSpringMapper {


    @Mappings({
            @Mapping(source = "tel", target = "telNumber"),
            @Mapping(source = "birthday", target = "birthday", dateFormat = "yyyy-MM-dd")
    })
    UserVo convertToVo(User user);
}
  • ②. 测试数据
    @Autowired
    private UserSpringMapper userMapper;
    @GetMapping("/mapStructToVoSpring")
    public String mapStructToVoSpring() {
        User user = new User();
        // {"birthday":"2023-10-12","email":"845195485@qq.com","id":1,"telNumber":"18774149733","userName":"tang"}
        user.setId(1).setEmail("845195485@qq.com").setUserName("tang").setBirthday(new Date()).setTel("18774149733");
        UserVo userVo = userMapper.convertToVo(user);
        System.out.println(JSON.toJSONString(userVo));
        return JSON.toJSONString(userVo);
    }

⑥. 常量、默认值和表达式

  • ①. 使用MapStruct映射属性时,我们可以设置属性为常量或者默认值,也可以通过Java中的方法编写表达式来自动生成属性
@Data
@Accessors(chain = true)
public class Product {
    private Long id;
    private String productSn;
    private String name;
    private String subTitle;
    private String brandName;
    private BigDecimal price;
    private Integer count;//商品数量
    private Date createTime;
}
  • ②. Product转换为ProductVo对象,id属性设置为常量,count设置默认值为1,productSn设置为UUID生成
@Data
public class ProductVo {
    //使用常量
    private Long id;
    //使用表达式生成属性
    private String productSn;
    private String name;
    private String subTitle;
    private String brandName;
    private BigDecimal price;
    //使用默认值
    private Integer number;//商品数量
    private Date createTime;
}
  • ③. 创建ProductMapper接口,通过@Mapping注解中的constant、defaultValue、expression设置好映射规则;
@Mapper(imports = {UUID.class})
public interface ProductMapper {
    ProductMapper INSTANCE = Mappers.getMapper(ProductMapper.class);

    @Mapping(target = "id",constant = "-1L")  //给转换后的productVo的id字段设置为常量-1
    @Mapping(source = "count",target = "number",defaultValue = "1")
    @Mapping(target = "productSn",expression = "java(UUID.randomUUID().toString())")
    ProductVo convertToVo(Product product);
}
  • ④. 测试数据
    @GetMapping("/defaultMapping")
    public Result defaultMapping() {
        Product product = new Product();
        product.setId(200L);
        product.setCount(null);
        ProductVo productVo = ProductMapper.INSTANCE.convertToVo(product);
        System.out.println(JSON.toJSONString(productVo));
        return Result.success(productVo);
    }

⑦. 自定义切面处理

  • ①. MapStruct也支持在映射前后做一些自定义操作,类似Spring的AOP中的切面

  • ②. 此时我们需要创建自定义处理方法,创建一个抽象类ProductRoundMapper,通过@BeforeMapping注解自定义映射前操作,通过@AfterMapping注解自定义映射后操作

@Mapper(imports = {UUID.class},unmappedTargetPolicy = ReportingPolicy.IGNORE)
public abstract class ProductRoundMapper {
    public static ProductRoundMapper INSTANCE = Mappers.getMapper(ProductRoundMapper.class);

    @Mappings({
            @Mapping(target = "id",constant = "-1L"),
            @Mapping(source = "count",target = "number",defaultValue = "1"),
            @Mapping(target = "productSn",expression = "java(UUID.randomUUID().toString())")
    })
    public abstract ProductVo convertToVo(Product product);

    @BeforeMapping
    public void beforeMapping(Product product){
        //映射前当price<0时设置为0
        if(product.getPrice().compareTo(BigDecimal.ZERO)<0){
            product.setPrice(BigDecimal.ZERO);
        }
    }

    @AfterMapping
    public void afterMapping(@MappingTarget ProductVo productVo){
        //映射后设置当前时间为createTime
        productVo.setCreateTime(new Date());
    }
}
  • ③. 测试
    @GetMapping("/aspectMapping")
    public String defaultMapping() {
        Product product = new Product();
        product.setId(100L);
        product.setCount(null);
        product.setPrice(new BigDecimal(-100) );
        ProductVo productVo = ProductRoundMapper.INSTANCE.convertToVo(product);
        // {"createTime":1697113274023,"id":-1,"number":1,"price":0,"productSn":"fe154c52-8808-40e1-b0a6-68b5e6437ea5"}
        System.out.println(JSON.toJSONString(productVo));
        return JSON.toJSONString(productVo);
    }
  • ④. 如果需要将一个List转为另外一个List,可以使用这种方式
 result  = xxxList.stream()
                    .map(XXX.INSTANCE::convertNew).collect(Collectors.toList());

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1085399.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SQL 教程||SQL 简介

SQL 简介&#xff08;开个新坑&#xff09; SQL&#xff08;结构化查询语言&#xff09;是用于访问和操作数据库中的数据的标准数据库编程语言。 SQL是关系数据库系统的标准语言。所有关系数据库管理系统(RDMS)&#xff0c;如MySQL、MS Access、Oracle、Sybase、Informix、Po…

第四章——密码学的数学引论

一.数论 1.素数 200以内的素数&#xff1a; 2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97 101 103 107 109 113 127 131 137 139 149 151 157 163 167 173 179 181 191 193 197 199 算术基本定理&#xff1a; 任何一个不等于0的正整数a都可以写…

接上面 通过索引index 和类型Java.lang.string.来注入

index 属性表示构造函数参数的索引&#xff0c;value 属性表示构造函数参数的值。

【web实现右侧弹窗】JS+CSS如何实现右侧缓慢弹窗动态效果『附完整源码下载』

文章目录 写在前面涉及知识点页面效果1、页面DOM创建1.1创建底层操作dom节点1.2 创建存放弹窗dom节点 2、页面联动功能实现&#xff08;关闭与弹出&#xff09;2.1 点击非右侧区域实现关闭2.2 点击叉叉及关闭按钮实现关闭功能 3、完整源码包下载3.1百度网盘3.2 123云盘3.3邮箱留…

没用的知识增加了,尝试用文心实现褒义词贬义词快速分类

尝试用文心实现褒义词贬义词快速分类 一、我的需求二、项目环境搭建千帆SDK安装及使用流程 三、项目实现过程创建应用获取签名调用接口计算向量积总结 百度世界大会将于10月17日在北京首钢园举办&#xff0c;今天进入倒计时五天了。通过官方渠道的信息了解到&#xff0c;这次是…

【APUE】文件系统 — 进程环境

目录 一、再提 main 函数 二、进程的终止 2.1 正常终止 2.1.1 从 main 函数用 return 返回 2.1.2 主动调用 exit 函数 2.1.3 钩子函数 2.1.4 调用 _exit 或 _Exit 2.2 异常终止 三、命令行参数的分析 3.1 getopt 3.2 示例 四、环境变量 4.1 简介 4.2 查看环境…

内网收集哈希传递

1.内网收集的前提 获得一个主机权限 补丁提权 可以使用 systeminfo 然后使用python脚本找到缺少的补丁 下载下来 让后使用exp提权 收集信息 路由信息 补丁接口 dns域看一看是不是域控 扫描别的端口 看看有没有内在的web网站 哈希传递 哈希是啥 哈希…

墨者学院 WordPress 远程命令执行漏洞(CVE-2018-15877)

1. 背景介绍 近日&#xff0c;WordPress 插件Plainview Activity Monitor被曝出存在一个远程命令执行漏洞。Plainview Activity Monitor 是一款网站用户活动监控插件。 远程攻击者可以通过构造的url来诱导wordpress管理员来点击恶意链接最终导致远程命令执行 2.影响范围 Pla…

ajax同步与异步,json-serve的安装与使用,node.js的下载

20.ajax json 轻量级的数据格式做配置文件网络传输 xml 重量级的数据格式 可扩展标记语言做配置文件网络传输 现在目前主流就是大量采用json做网络传输数据格式 1.ajax的概念: 与服务器进行’通信’的一种技术,能够实现异步的刷新页面 **同步:**按照顺序一步步的执行,容易造…

Python应用-矩阵乘法-特征提取

目录 常规运算应用场景&#xff1a;特征提取特征矩阵权重矩阵举例说明代码展示 常规运算 import numpy as npmatrix1 np.array([[1, 2], [3, 4]]) matrix2 np.array([[5, 6], [7, 8]]) result np.dot(matrix1, matrix2) print(result) 输出结果&#xff1a; [[19 22][43 …

是谁还没听过杨氏矩阵~原理和实现代码都已经准备好了

有一个数字矩阵&#xff0c;矩阵的每行从左到右是递增的&#xff0c;矩阵从上到下是递增的&#xff0c;请编写程序在这样的矩阵中查找某个数字是否存在。 要求&#xff1a;时间复杂度小于O(N); 看到这个题目时&#xff0c;我们会马上想到暴力求解&#xff0c;即遍历这个矩阵的每…

Excel往Word复制表格时删除空格

1.背景 在编写文档&#xff0c;经常需要从Excel往Word里复制表格 但是复制过去的表格前面会出现空格&#xff08;缩进&#xff09; 再WPS中试了很多方法&#xff0c;终于摆脱了挨个删除的困扰 2. WPS排版中删除 选择表格菜单栏-选在【开始】-【排版】选择【更多段落处理】-【段…

论文阅读/写作扫盲

第一节&#xff1a;期刊科普 JCR分区和中科院分区是用于对期刊进行分类和评估的两种常见方法。它们的存在是为了帮助学术界和研究人员更好地了解期刊的学术质量、影响力和地位。 JCR分区&#xff08;Journal Citation Reports&#xff09;&#xff1a;JCR分区是由Clarivate Ana…

优雅而高效的JavaScript——箭头函数

&#x1f917;博主&#xff1a;小猫娃来啦 &#x1f917;文章核心&#xff1a;优雅而高效的JavaScript——箭头函数 文章目录 前言箭头函数的基本语法和特点箭头函数的语法箭头函数的词法绑定特性箭头函数的this值箭头函数无法使用arguments对象 箭头函数与传统函数的比较箭头函…

端口被占用?两步解决端口占用问题

第一步&#xff1a;WinR 打开命令提示符&#xff0c;输入netstat -ano|findstr 端口号&#xff0c;找到被占用端口的进程 第二步&#xff1a; 杀死使用该端口的进程&#xff0c;输入taskkill /t /f /im 进程号&#xff08; 注意是进程号&#xff0c;不是端口号&#xff09;

身份证号码,格式校验:@IdCard(自定义注解)

目标 自定义一个用于校验 身份证号码 格式的注解IdCard&#xff0c;能够和现有的 Validation 兼容&#xff0c;使用方式和其他校验注解保持一致&#xff08;使用 Valid 注解接口参数&#xff09;。 校验逻辑 有效格式 符合国家标准。 公民身份号码按照GB11643&#xff0d;…

Vue2实现图片预览功能 -- v-viewer:图片查看器

一. 先看效果图 二. 具体步骤 简介&#xff1a;一款基于 viewer.js 封装的Vue版插件&#xff0c;可用于图像查看&#xff0c;以及图片的旋转、缩放等功能预览 官网&#xff1a;v-viewer 文档说明&#xff1a;Vue图片浏览组件v-viewer&#xff0c;支持旋转、缩放、翻转等操作 - …

字符设备和杂项设备总结

字符设备是 3 大类设备&#xff08;字符设备、块设备和网络设备&#xff09;中的一类&#xff0c;其驱动程序完成的主要工作是初始化、添加和删除 cdev 结构体&#xff0c;申请和释放设备号&#xff0c;以及填充 file_operations 结构体中的操作函数&#xff0c;实现file_opera…

# Web server failed to start. Port 9793 was already in use

Web server failed to start. Port 9793 was already in use. 文章目录 Web server failed to start. Port 9793 was already in use.报错描述报错原因解决方法Spring Boot 修改默认端口号关闭占用某一端口号的进程关闭该进程 报错描述 Springboot项目启动控制台报错 Error st…

LeetCode【15】三数之和

题目&#xff1a; 解析&#xff1a; 参考&#xff1a;https://zhuanlan.zhihu.com/p/111715985 代码&#xff1a; public static List<List<Integer>> threeSum(int[] nums) {// 先排序Arrays.sort(nums);List<List<Integer>> result new ArrayLis…