mapstruct个人学习记录

mapstruct核心技术学习

  • 简介
  • 入门案例
    • maven依赖
  • IDEA插件
    • 单一对象转换
      • 测试结果
  • mapping属性
  • Spring注入的方式
    • 测试
  • 集合的映射
    • set类型的映射
    • 测试
    • map类型的映射
      • 测试
    • @MapMapping
      • keyDateFormat
      • valueDateFormat
  • 枚举映射
    • 基础入门

简介

在工作中,我们经常要进行各种对象之间的转换。
PO: persistent object持久对象,对应数据库中的一条
VO: view object表现层对象,最终返回给前端的对象
DTO:data transfer object数据传输对象,如dubbo服务之间的传输的对象
po、vo、dto的详细介绍
如果这些对象的属性名相同还好,可以使用如下工具类赋值
Spring BeanUtils
Cglib BeanCopier
避免使用Apache BeanUtils,性能较差

如果属性名不同呢?如果是将多个PO对象合并成一个VO对象呢?好在有MapStruct,可以帮助我们快速转换
mapstruct官网
mapstruct技术文档

入门案例

maven依赖

 <properties><java.version>1.8</java.version><org.mapstruct.version>1.5.5.Final</org.mapstruct.version>
</properties>
<dependencies><dependency><groupId>org.mapstruct</groupId><artifactId>mapstruct</artifactId><version>${org.mapstruct.version}</version></dependency><dependency><groupId>org.mapstruct</groupId><artifactId>mapstruct-processor</artifactId><version>${org.mapstruct.version}</version></dependency>
</dependencies>        

IDEA插件

IDEA中搜索"MapStruct Support"插件,进行安装,安装成功后重启IDEA。
在这里插入图片描述

单一对象转换

import lombok.Data;
import lombok.experimental.Accessors;@Data
@Accessors(chain = true)
public class CarDTO {private String make;private int seatCount;private String type;
}
import lombok.Data;
import lombok.experimental.Accessors;@Data
@Accessors(chain = true)
public class Car {private String make;private int numberOfSeats;
}
import com.example.demo.dto.CarDTO;
import com.example.demo.po.Car;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.factory.Mappers;@Mapper
public interface CarMapper {CarMapper instance = Mappers.getMapper(CarMapper.class);/*** 表达式需要自动提示的话,需要安装IDEA插件mapstruct support* @param car* @return*/@Mapping(source = "numberOfSeats",target = "seatCount")@Mapping(target = "type",expression = "java(car.getMake())")CarDTO carToCarDto(Car car);
}
import com.example.demo.dto.CarDTO;
import com.example.demo.mapper.CarMapper;
import com.example.demo.po.Car;
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(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = {DemoApplication.class})
public class ApplicationTest {@Testpublic void test() {CarMapper instance = CarMapper.instance;Car car = new Car();car.setMake("中国").setNumberOfSeats(1000);CarDTO carDTO = instance.carToCarDto(car);System.out.println(carDTO);}
}

测试结果

在这里插入图片描述
项目结构图
在这里插入图片描述
在target文件夹下生成了CarMapperImpl.java

package com.example.demo.mapper;import com.example.demo.dto.CarDTO;
import com.example.demo.po.Car;
import javax.annotation.Generated;@Generated(value = "org.mapstruct.ap.MappingProcessor",date = "2023-11-08T23:35:28+0800",comments = "version: 1.5.5.Final, compiler: javac, environment: Java 1.8.0_131 (Oracle Corporation)"
)
public class CarMapperImpl implements CarMapper {@Overridepublic CarDTO carToCarDto(Car car) {if ( car == null ) {return null;}CarDTO carDTO = new CarDTO();carDTO.setSeatCount( car.getNumberOfSeats() );carDTO.setMake( car.getMake() );carDTO.setType( car.getMake() );return carDTO;}
}

mapping属性

    /*** @Mappings 一组映射关系,值为一个数组,元素为@Mapping* @Mapping 一一对应关系* source:源属性* target:目标属性,赋值的过程是把源属性赋值给目标属性* dateFormat:用于源属性是Date,转换为String* numberFormat:用户数值类型与String类型之间的转* constant: 常量* expression:使用表达式进行属性之间的转换* ignore:忽略某个属性的赋值* qualifiedByName: 自定义的方法赋值* defaultValue:默认值* @defaultExpression 如果源数据没有设置的时候,可以指定相关表达式进行处理* 基本数据类型与包装类可以自动映射* @MappingTaget 用在方法参数的前面,使用此注解,源对象同时也会作为目标对象,用于更新* @InheritConfiguration 指定映射方法* @InheritInverseConfiguration 表示方法继承相应的反向方法的反向配置* @param car 入参* @return 返回结果*/
package com.example.demo.entity;import lombok.Data;
import lombok.experimental.Accessors;@Data
@Accessors(chain = true)
public class CarBrand {private String carBrand;
}
package com.example.demo.entity;import lombok.Data;
import lombok.experimental.Accessors;@Data
@Accessors(chain = true)
public class Brand {private String brandName;}
package com.example.demo.entity;import lombok.Data;
import lombok.experimental.Accessors;@Data
@Accessors(chain = true)
public class CarDto {private String make;private int seatCount;private String type;private CarBrand carBrand;private String date;private String price;private String extral;
}
package com.example.demo.entity;import lombok.Data;
import lombok.experimental.Accessors;import java.math.BigDecimal;
import java.util.Date;@Data
@Accessors(chain = true)
public class Car {private String make;private int numberOfSeats;private Brand brand;private Date date;private BigDecimal price;}
package com.example.demo.entity;import org.mapstruct.*;import static org.mapstruct.MappingConstants.ComponentModel.SPRING;/*** @Mapper 表示该接口作为映射接口,编译时MapStruct处理器的入口* componentModel 主要是指定实现类的类型,一般用到两个* default:默认,可以通过 Mappers.getMapper(Class) 方式获取实例对象* spring:在接口的实现类上自动添加注解 @Component,可通过 @Autowired 方式注入* uses 外部引入的转换类*/
@Mapper(componentModel = SPRING)
public interface CarMapper {/*** @Mappings 一组映射关系,值为一个数组,元素为@Mapping* @Mapping 一一对应关系* source:源属性* target:目标属性,赋值的过程是把源属性赋值给目标属性,当目标属性和源属性一致时候,source和target可以省略不写* dateFormat:用于源属性是Date,转换为String* numberFormat:用户数值类型与String类型之间的转* constant: 常量* expression:使用表达式进行属性之间的转换* ignore:忽略某个属性的赋值* qualifiedByName: 自定义的方法赋值* defaultValue:默认值* @defaultExpression 如果源数据没有设置的时候,可以指定相关表达式进行处理* 基本数据类型与包装类可以自动映射* @MappingTaget 用在方法参数的前面,使用此注解,源对象同时也会作为目标对象,用于更新* @InheritConfiguration 指定映射方法* @InheritInverseConfiguration 表示方法继承相应的反向方法的反向配置* @param car 入参* @return 返回结果*/@Mappings({@Mapping(source = "date",target = "date",dateFormat = "yyyy-MM-dd HH:mm:ss"),@Mapping(source = "price",target = "price",numberFormat = "0.00"),@Mapping(source = "numberOfSeats",target = "seatCount"),@Mapping(target = "type",constant = "hello type"),@Mapping(source = "brand",target = "carBrand",qualifiedByName = {"brand2CarBrandV2"})})CarDto carToCarDtoV2(Car car);/*** @Named 定义类/方法的名称* @param brand* @return*/@Named("brand2CarBrandV2")@Mappings({@Mapping(source = "brandName",target = "carBrand")})CarBrand brand2CarBrandV2(Brand brand);
}
package com.example.demo;import com.example.demo.entity.Brand;
import com.example.demo.entity.Car;
import com.example.demo.entity.CarDto;
import com.example.demo.entity.CarMapper;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;import java.math.BigDecimal;
import java.util.Date;@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = {DemoApplication.class})
public class CarMapperTest {@Autowiredprivate CarMapper carMapper;@Testpublic void test() {Car car = new Car();car.setMake("from source").setNumberOfSeats(100).setBrand(new Brand().setBrandName("保密")).setPrice(BigDecimal.valueOf(100.12345)).setDate(new Date());CarDto dto = carMapper.carToCarDtoV2(car);System.out.println(dto);}
}

测试结果,输出如下

CarDto(make=from source, seatCount=100, type=hello type, carBrand=CarBrand(carBrand=保密), date=2023-11-11 20:22:49, price=100.12, extral=null)

Spring注入的方式

中声明INSTANCE的方式来进行调用之外,MapStruct也同时支持Spring的依赖注入机制

package com.example.MapStructDemo.dto;import lombok.Data;
import lombok.experimental.Accessors;@Data
@Accessors(chain = true)
public class CarDto {private String manufacturer;private int seatCount;
}
package com.example.MapStructDemo.po;import lombok.Data;
import lombok.experimental.Accessors;@Data
@Accessors(chain = true)
public class Car {private String make;private int numberOfSeats;
}
package com.example.MapStructDemo.mapper;import com.example.MapStructDemo.dto.CarDto;
import com.example.MapStructDemo.po.Car;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;import static org.mapstruct.MappingConstants.ComponentModel.SPRING;/*** @Mapper 只有在接口加上这个注解, MapStruct 才会去实现该接口* @Mapper 里有个 componentModel 属性,主要是指定实现类的类型,一般用到两个* default:默认,可以通过 Mappers.getMapper(Class) 方式获取实例对象* SPRING:在接口的实现类上自动添加注解 @Component,可通过 @Autowired 方式注入*/
@Mapper(componentModel = SPRING)
public interface CarMapper {@Mapping(target = "manufacturer",source = "make")@Mapping(target = "seatCount",source = "numberOfSeats")CarDto carToCarDto(Car car);}

测试

package com.example.MapStructDemo;import com.example.MapStructDemo.dto.CarDto;
import com.example.MapStructDemo.mapper.CarMapper;
import com.example.MapStructDemo.po.Car;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;@SpringBootTest
public class ApplicationTests {@Autowiredprivate CarMapper carMapper;@Testpublic void contextLoads() {Car car = new Car();car.setMake("中国").setNumberOfSeats(1000);CarDto carDto = carMapper.carToCarDto(car);System.out.println("测试结果");System.out.println(carDto);}}

在这里插入图片描述

集合的映射

set类型的映射

package com.example.MapStructDemo.mapper;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import java.util.Set;
import static org.mapstruct.MappingConstants.ComponentModel.SPRING;@Mapper(componentModel = SPRING)
public interface CarMapper {/*** 集合的映射* @param set 入参* @return Set<String>*/Set<String> integerSetToStringSet(Set<Integer> set);
}

CarMapper的实现类CarMapperImpl,会生成如下代码,集合set为null的时候,默认返回null
在这里插入图片描述

测试

package com.example.MapStructDemo;import com.example.MapStructDemo.mapper.CarMapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;import java.util.HashSet;
import java.util.Set;@SpringBootTest
public class ApplicationTests {@Autowiredprivate CarMapper carMapper;@Testpublic void test(){Set<Integer> set = new HashSet<>();for (int i=0;i<10;i++){set.add(i);}Set<String> strings = carMapper.integerSetToStringSet(set);System.out.println("集合类型的测试");strings.forEach(System.out::println);}
}

测试结果如下:
在这里插入图片描述

map类型的映射

package com.example.MapStructDemo.mapper;import org.mapstruct.MapMapping;
import org.mapstruct.Mapper;import java.util.Date;
import java.util.Map;import static org.mapstruct.MappingConstants.ComponentModel.SPRING;@Mapper(componentModel = SPRING)
public interface SourceTargetMapper {/*** map类型的映射* @param source 入参* @return Map<String, String>* map中value的值是Date类型的转换为String类型*/@MapMapping(valueDateFormat = "dd.MM.yyyy")Map<String, String> longDateMapToStringStringMap(Map<Long, Date> source);
}

map映射的实现类SourceTargetMapperImpl

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//package com.example.MapStructDemo.mapper;import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import org.springframework.stereotype.Component;@Component
public class SourceTargetMapperImpl implements SourceTargetMapper {public SourceTargetMapperImpl() {}public Map<String, String> longDateMapToStringStringMap(Map<Long, Date> source) {if (source == null) {return null;} else {Map<String, String> map = new LinkedHashMap(Math.max((int)((float)source.size() / 0.75F) + 1, 16));Iterator var3 = source.entrySet().iterator();while(var3.hasNext()) {Map.Entry<Long, Date> entry = (Map.Entry)var3.next();String key = (new DecimalFormat("")).format(entry.getKey());String value = (new SimpleDateFormat("dd.MM.yyyy")).format((Date)entry.getValue());map.put(key, value);}return map;}}
}

测试

package com.example.MapStructDemo;import com.example.MapStructDemo.mapper.SourceTargetMapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;import java.time.LocalDate;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;@SpringBootTest
public class SourceTargetMapperTests {@Autowiredprivate SourceTargetMapper sourceTargetMapper;@Testpublic void test() {Map<Long, Date> map = new HashMap<>();map.put(1L, new Date());System.out.println(map);System.out.println("map类型的映射");Map<String, String> result = sourceTargetMapper.longDateMapToStringStringMap(map);System.out.println(result);}
}

测试结果
在这里插入图片描述

@MapMapping

配置的是Map<String,String>Map<Long,Date>之间的转换

keyDateFormat

mapkey的类型是从DateString类型的转换

valueDateFormat

map中value的类型从DateString类型的转换

枚举映射

基础入门

package com.example.MapStructDemo.enums;import lombok.AllArgsConstructor;
import lombok.Getter;@AllArgsConstructor
@Getter
public enum OrderType {EXTRA,STANDARD,NORMAL
}
package com.example.MapStructDemo.enums;import lombok.AllArgsConstructor;
import lombok.Getter;@AllArgsConstructor
@Getter
public enum ExternalOrderType {SPECIAL,DEFAULT
}
package com.example.MapStructDemo.mapper;import com.example.MapStructDemo.enums.ExternalOrderType;
import com.example.MapStructDemo.enums.OrderType;
import org.mapstruct.Mapper;
import org.mapstruct.ValueMapping;
import org.mapstruct.ValueMappings;import static org.mapstruct.MappingConstants.ComponentModel.SPRING;@Mapper(componentModel = SPRING)
public interface OrderMapper {/*** 枚举类型映射* @param orderType 入参* @return ExternalOrderType*/@ValueMappings({@ValueMapping(target = "SPECIAL",source = "EXTRA"),@ValueMapping(target = "DEFAULT",source = "STANDARD"),@ValueMapping(target = "DEFAULT",source = "NORMAL")})ExternalOrderType orderTypeToExternalOrderType(OrderType orderType);
}

OrderMapper的实现类

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//package com.example.MapStructDemo.mapper;import com.example.MapStructDemo.enums.ExternalOrderType;
import com.example.MapStructDemo.enums.OrderType;
import org.springframework.stereotype.Component;@Component
public class OrderMapperImpl implements OrderMapper {public OrderMapperImpl() {}public ExternalOrderType orderTypeToExternalOrderType(OrderType orderType) {if (orderType == null) {return null;} else {ExternalOrderType externalOrderType;switch (orderType) {case EXTRA:externalOrderType = ExternalOrderType.SPECIAL;break;case STANDARD:externalOrderType = ExternalOrderType.DEFAULT;break;case NORMAL:externalOrderType = ExternalOrderType.DEFAULT;break;default:throw new IllegalArgumentException("Unexpected enum constant: " + orderType);}return externalOrderType;}}
}
package com.example.MapStructDemo;import com.example.MapStructDemo.enums.ExternalOrderType;
import com.example.MapStructDemo.enums.OrderType;
import com.example.MapStructDemo.mapper.OrderMapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;@SpringBootTest
public class OrderMapperTest {@Autowiredprivate OrderMapper orderMapper;@Testpublic void test1() {System.out.println("测试结果");OrderType orderType = OrderType.EXTRA;ExternalOrderType externalOrderType = orderMapper.orderTypeToExternalOrderType(orderType);System.out.println(externalOrderType);}
}

测试结果
在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Sql Server关于表的建立、修改、删除

表的创建&#xff1a; &#xff08;1&#xff09;在“对象资源管理器”面板中展开“数据库”节点&#xff0c;可以看到自己创建的数据库&#xff0c;比如Product。展开Product节点&#xff0c;右击“表”节点&#xff0c;在弹出的快捷菜单中选择“新建表”项&#xff0c;进入“…

JMS(Java Message Service)使用指南

介绍 JMS即Java消息服务&#xff08;Java Message Service&#xff09;应用程序接口&#xff0c;是一个Java平台中关于面向消息中间件&#xff08;MOM&#xff09;的API&#xff0c;用于在两个应用程序之间&#xff0c;或分布式系统中发送消息&#xff0c;进行异步通信。它是一…

SwinIR: Image Restoration Using Swin Transformer

SwinIR 简介 论文地址&#xff1a;SwinIR: Image Restoration Using Swin Transformer 代码&#xff1a;SwinIR ​ 本文提出了一个基于swin transformer的图像超分模型swinIR。其中SwinIR分为三部分&#xff1a;浅层特征提取、深层特征提取和高质量图像重建模块。 现阶段问…

【C++11(三)】智能指针详解--RAII思想循环引用问题

&#x1f493;博主CSDN主页:杭电码农-NEO&#x1f493;   ⏩专栏分类:C从入门到精通⏪   &#x1f69a;代码仓库:NEO的学习日记&#x1f69a;   &#x1f339;关注我&#x1faf5;带你学习C   &#x1f51d;&#x1f51d; C11 1. 前言2. 为什么要有智能指针?3. RAII思想…

使用Python提取PDF文件中指定页面的内容

在日常工作和学习中&#xff0c;我们经常需要从PDF文件中提取特定页面的内容。在本篇文章中&#xff0c;我们将介绍如何使用Python编程语言和两个强大的库——pymupdf和wxPython&#xff0c;来实现这个任务。 1. 准备工作 首先&#xff0c;确保你已经安装了以下两个Python库&…

保姆级 | XSS Platform环境搭建

0x00 前言 XSS Platform 平台主要是用作验证跨站脚本攻击。该平台可以部署在本地或服务器环境中。我们可以使用 XSS Platfrom 平台搭建、学习或验证各种类型的 XSS 漏洞。 0x01 环境说明 HECS(云耀云服务器)xss platformUbuntu 22.04Nginx 1.24.0MySQL 5.6.51Pure-Ftpd 1.0.49…

LeetCode力扣每日一题(Java):35、搜索插入位置

一、题目 二、解题思路 1、我的思路&#xff08;又称&#xff1a;论API的重要性&#xff09; 读完题目之后&#xff0c;我心想这题目怎么看着这么眼熟&#xff1f;好像我之前学过的一个API呀&#xff01; 于是我回去翻了翻我之前写的博客&#xff1a;小白备战蓝桥杯&#xf…

Draw.io or diagrams.net 使用方法

0 Preface/Foreword 在工作中&#xff0c;经常需要用到框图&#xff0c;流程图&#xff0c;时序图&#xff0c;等等&#xff0c;draw.io可以完成以上工作。 official website:draw.io 1 Usage 1.1 VS code插件 draw.io可以扩展到VS code工具中。

Python自动化:selenium常用方法总结

使用的Python版本为3.8&#xff0c;selenium版本为4.15.2 Python自动化:selenium常用方法总结 1. 三种等待方式2. 浏览器操作3. 8种查找元素的方法4. 高级事件 1. 三种等待方式 强制等待 使用模块time下的sleep()实现等待效果隐式等待 使用driver.implicitly_wait()方法&#…

ChibiOS简介2/5

ChibiOS简介2/5 1. 源由2. ChibiOS基础知识2/52.4 Chapter 4 - ChibiOS General Architecture2.4.1 The Big Picture&#xff08;总体框图&#xff09;2.4.2 Embedded Components&#xff08;嵌入式组件&#xff09;2.4.3 Application Model&#xff08;应用模型&#xff09;2.…

Vue之数据绑定

在我们Vue当中有两种数据绑定的方法 1.单向绑定 2.双向绑定 让我为大家介绍一下吧&#xff01; 1、单向绑定(v-bind) 数据只能从data流向页面 举个例子&#xff1a; <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"…

【Kubernetes】持久化存储emptyDir/hostPath/nfs/PVC

k8s持久化存储 一、为什么做持久化存储&#xff1f;二、k8s持久化存储&#xff1a;emptyDir三、k8s持久化存储&#xff1a;hostPath四、k8s持久化存储&#xff1a;nfs4.1、搭建nfs服务4.2、挂载nfs共享目录 五、k8s持久化存储&#xff1a; PVC5.1、什么是PV5.2、什么是PVC5.3、…