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.8java.version>
        <org.mapstruct.version>1.5.5.Finalorg.mapstruct.version>
properties>
<dependencies>
		<dependency>
            <groupId>org.mapstructgroupId>
            <artifactId>mapstructartifactId>
            <version>${org.mapstruct.version}version>
        dependency>
        <dependency>
            <groupId>org.mapstructgroupId>
            <artifactId>mapstruct-processorartifactId>
            <version>${org.mapstruct.version}version>
        dependency>
dependencies>        

IDEA插件

IDEA中搜索"MapStruct Support"插件,进行安装,安装成功后重启IDEA。
mapstruct个人学习记录_第1张图片

单一对象转换

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 {

    @Test
    public void test() {
        CarMapper instance = CarMapper.instance;
        Car car = new Car();
        car.setMake("中国")
                .setNumberOfSeats(1000);
        CarDTO carDTO = instance.carToCarDto(car);
        System.out.println(carDTO);
    }
}

测试结果

在这里插入图片描述
项目结构图
mapstruct个人学习记录_第2张图片
在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 {

    @Override
    public 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 {

    @Autowired
    private CarMapper carMapper;

    @Test
    public 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 {
    @Autowired
    private CarMapper carMapper;

    @Test
    public 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
     */
    Set<String> integerSetToStringSet(Set<Integer> set);
}

CarMapper的实现类CarMapperImpl,会生成如下代码,集合set为null的时候,默认返回null
mapstruct个人学习记录_第3张图片

测试

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 {
    @Autowired
    private CarMapper carMapper;

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

测试结果如下:
mapstruct个人学习记录_第4张图片

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
     * 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 {

    @Autowired
    private SourceTargetMapper sourceTargetMapper;

    @Test
    public 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

配置的是MapMap之间的转换

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 {

    @Autowired
    private OrderMapper orderMapper;

    @Test
    public void test1() {
        System.out.println("测试结果");
        OrderType orderType = OrderType.EXTRA;
        ExternalOrderType externalOrderType = orderMapper.orderTypeToExternalOrderType(orderType);
        System.out.println(externalOrderType);
    }
}

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

你可能感兴趣的:(java,java)