MapStruct使用

前言

MapStruct是一个Java注释处理器,用于生成类型安全的bean映射类。

您要做的就是定义一个映射器接口,该接口声明任何必需的映射方法。在编译期间,MapStruct将生成此接口的实现。此实现使用简单的Java方法调用在源对象和目标对象之间进行映射,即没有反射或类似内容。

与手动编写映射代码相比,MapStruct通过生成繁琐且易于出错的代码来节省时间。遵循配置方法上的约定,MapStruct使用合理的默认值,但在配置或实现特殊行为时不加理会。
与动态映射框架相比,MapStruct具有以下优点:

  1. 通过使用普通方法调用(settter/getter)而不是反射来快速执行
  2. 编译时类型安全性:只能映射相互映射的对象和属性,不能将order实体意外映射到customer DTO等。
  3. 如果有如下问题,编译时会抛出异常
    3.1 映射不完整(并非所有目标属性都被映射)
    3.2 映射不正确(找不到正确的映射方法或类型转换)
  4. 可以通过freemarker定制化开发

准备:maven依赖

            <!--mapStruct依赖 高性能对象映射-->
            <!--mapstruct核心-->
            <dependency>
                <groupId>org.mapstruct</groupId>
                <artifactId>mapstruct</artifactId>
                <version>1.5.0.Beta1</version>
            </dependency>
            <!--mapstruct编译-->
            <dependency>
                <groupId>org.mapstruct</groupId>
                <artifactId>mapstruct-processor</artifactId>
                <version>1.5.0.Beta1</version>
            </dependency>

Lombok依赖:(版本最好在1.16.16以上,否则会出现问题)通常是和lombok一起使用

        <dependency>
            <groupid>org.projectlombok</groupid>
            <artifactid>lombok</artifactid>
            <version>${lombok.version}</version>
          	// 版本号 1.18.12
        </dependency>

可以下载 mapstruct support 插件

 项目中使用

实体类

package com.erp.platform.service.report.impl.mapstruct;

/**
 * @Package com.erp.platform.common.params.sys.payrollReport.ecsReport
 * @Author guang
 * @Description 描述:
 * @Date 2023/1/31 21:06
 */
public class User {

    private String name;

    private String age;

    @Convert(converter = SubjectEnum.Convert.class)
    private SubjectEnum subject;

    public String getName() {
        return name;
    }

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

    public String getAge() {
        return age;
    }

    public void setAge(String age) {
        this.age = age;
    }
}
package com.erp.platform.service.report.impl.mapstruct;

import lombok.Data;

/**
 * @Package com.erp.platform.service.report.impl.mapstruct
 * @Author guang
 * @Description 描述:
 * @Date 2023/2/4 21:05
 */
@Data
public class UserVO {
    private String name;

    private String nianLing;

    private SubjectEnum subject;

    private String createStamp;
}
package com.erp.platform.service.report.impl.mapstruct;

import lombok.Data;

/**
 * @Package com.erp.platform.service.report.impl.mapstruct
 * @Author guang
 * @Description 描述:
 * @Date 2023/2/4 22:57
 */
@Data
public class UserUpdateVO {
    private String name;

    private String age;

    private SubjectEnum subject;

    private String createStamp;
}

 

枚举类

package com.erp.platform.service.report.impl.mapstruct;

/**
 * @Package com.erp.platform.service.report.impl.mapstruct
 * @Author guang
 * @Description 描述:
 * @Date 2023/2/1 22:00
 */
public enum SubjectEnum implements DbCodeField{
    English("1","英语"),
    mathematics("2","数学"),
    language("3","语文");

    private String code;
    private String name;

    SubjectEnum(String code, String name){
        this.code = code;
        this.name = name;
    }

    public String getCode() {
        return code;
    }

    public String getName() {
        return name;
    }

    public static class Convert extends DbCodeFieldConverter<SubjectEnum> {}
}

枚举类继承类

package com.erp.platform.service.report.impl.mapstruct;

import lombok.SneakyThrows;

import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.text.MessageFormat;
import java.util.Arrays;
import java.util.Objects;

/**
 * @Package com.erp.platform.service.report.impl.mapstruct
 * @Author guang
 * @Description 描述:
 * @Date 2023/2/1 22:10
 */
@Converter
public class DbCodeFieldConverter<E extends DbCodeField> implements AttributeConverter<E, String> {

    /**
     * E的class对象
     */
    private final Class<E> eClazz;

    /**
     * 枚举类的 values() 方法
     */
    private final Method valuesMethod;

    public DbCodeFieldConverter() {
        this.eClazz = (Class<E>) ((ParameterizedType) this.getClass()
                .getGenericSuperclass()).getActualTypeArguments()[0];
        try {
            this.valuesMethod = this.eClazz.getMethod("values");
        } catch (NoSuchMethodException e) {
            throw new RuntimeException("非枚举类无法使用Converter", e);
        }
    }

    @Override
    public String convertToDatabaseColumn(E attribute) {
        if (Objects.nonNull(attribute)) {
            return attribute.getCode();
        }
        return null;
    }

    @SneakyThrow
    @Override
    public E convertToEntityAttribute(String dbData) {
        if (Objects.nonNull(dbData)) {
            E[] elements = getEnumElements();
            return Arrays.stream(elements).filter(e -> e.getCode().equals(dbData))
                    .findFirst().orElseThrow(() ->  new BizException(CommonResponseCode.FAILURE.getCode()),
                            MessageFormat.format("未找到与数据库中代码值匹配的枚举元素,代码值:[f03],枚举类: []",
                                    dbData, this.eClazz));
        }
        return null;
    }

    @SneakyThrows
    private E[] getEnumElements(){
        return (E[]) valuesMethod.invoke( null);
    }
}

package com.erp.platform.service.report.impl.mapstruct;

/**
 * 数据库表中代码类字段安现该按口以使用Converter。
 * 数据字典要求所有代码类字度《枚举类》在数据库中以varchar或bpchac(定长字申)类型保存,对应java string
 * 在使用3PA的情况下,将Entity中枚举类型字段映射到数据库,还要保留诸如"01","02"这类任意映射
 * 最适合方式是自已号Convertor
 */
public interface DbCodeField {
    /**
     * 获取枚举中代表数据库代码值的字段的值
     * 如果那个字段就叫code,正好
     *
     * @return 代码值
     */
    String getCode();
}

转换类

package com.erp.platform.service.report.impl.mapstruct;

// import org.mapstruct.Mapper;
// import org.mapstruct.Mapping;
// import org.mapstruct.Mappings;

import org.mapstruct.*;
import org.mapstruct.factory.Mappers;

/**
 * @Package com.erp.platform.service.report.impl.mapstruct
 * @Author guang
 * @Description 描述:
 * @Date 2023/2/4 20:40
 */
public interface UserConverter {
    UserConverter INSTANCE = Mappers.getMapper(UserConverter.class);

    /**
     * 用户 DO 转 VO
     * 2023/2/4
     * @since 1.0.0
     */
    @Mappings(value = {
            // 设置时间格式
            @Mapping(source = "createStamp", target = "createStamp", dateFormat = "yyyy-MM-dd"),
            // 设置属性名不一样的两属性
            @Mapping(source = "age", target = "nianLing"),
            // 运用java设置某值
            @Mapping(target = "subject", expression = "java(user.getSubject().getCode())"),
            // 忽略某属性
            @Mapping(target = "name", ignore = true),
    })
    UserVO doToVO (User user);

    @Mappings(value = {
            // code转枚举
            @Mapping(target = "subject", expression = "java(new com.erp.platform.service.report.impl.mapstruct" +
                    ".SubjectEnum.Convert().convertToEntityAttribute(userVO.getSubject()))"),
            // 忽略某属性
            @Mapping(target = "name", ignore = true),
    })
    User voToDO (UserVO userVO);

    // 修改操作时null值的不做保存,user是查出来的值
    @BeanMapping(nullValueCheckStrategy = NullValueCheckStrategy.ALWAYS,nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE)
    User updateVOToDO (UserUpdateVO updateVO, @MappingTarget User user);


}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值