JavaBean转换工具Mapstruct使用教程

Mapstruct是一个注解处理器,用于生成类型安全、高性能的JavaBean映射代码,避免手动编写setter/getter的繁琐工作。它在编译时检查映射的完整性和准确性,提供更好的调试体验。文章介绍了Mapstruct的优点,如高性能、代码独立,以及如何使用它进行对象转换,包括配合Lombok的使用、注解参数解释、注入方式、案例展示和常见问题解答。
摘要由CSDN通过智能技术生成

本文为 【Mapstruct】 工具的相关知识,Mapstruct是一个生成类型安全,高性能且无依赖的 JavaBean 映射代码的注解处理器,可以生成 JavaBean 之间那的映射代码,类型安全,高性能,无依赖性。


📌博主主页:一个肥鲇鱼

👉开发中的坏习惯:程序员的坏习惯,你占了几个!

👉感受 Lambda 之美:体验一下Lambda之美吧,优雅编程

👉统一返回格式:SpringBoot统一返回格式(优雅实现)


前言

先来说一下为什么使用Mapstruct?

对于代码中 JavaBean之间的转换, 在编程中是一件很费时费力的。在开发的时候业务代码之间有很多的 JavaBean 之间的相互转化, 非常的影响观感,却又不得不存在,而手动的去(settter/getter)繁琐且易于出错。

优点分析

性能高
        这是相对反射来说的,反射需要去读取字节码的内容,花销会比较大。而通过 MapStruct 来生成的代码,其类似于人手写(settter/getter)。速度上可以得到保证。

代码独立
         生成的代码是对立的,没有运行时的依赖

当出现以下情况,程序在编译期间就会给出提示。
       映射不完整(并非所有目标属性都被映射)
       映射不正确(找不到合适的映射方法或类型转换)

易于Debug

ps:如果是配合Lombok使用的话,版本最好在1.16.16以上,否则Mapstruct编译会出现问题

依赖与插件

        <dependency>
            <groupId>org.mapstruct</groupId>
            <artifactId>mapstruct</artifactId>
            <version>1.5.2.Final</version>
        </dependency>
        <dependency>
            <groupId>org.mapstruct</groupId>
            <artifactId>mapstruct-processor</artifactId>
            <version>1.5.2.Final</version>
        </dependency>

 mapstruct support )插件不是必须,但挺好用

注解参数解释
@Mapping

当属性在目标实体种具有不同的名称时,可以通过@Mapping注释指定其名称

@Mappingsource()源目标实体属性
@Mappingtarget()目标实体属性
@MappingdefaultValue()默认值:源目标属性为null时,添加默认值
@MappingdateFormat()如果属性从字符串映射到日期,则该格式字符串可由SimpleDateFormat处理,反之亦然

注入方式

import org.mapstruct.Mapper;

/**
 * @Author: 一个肥鲇鱼
 */
@Mapper
public interface BeanConvert {

    BeanConvert COPY_PROPERTIES = Mappers.getMapper(BeanConvert.class);
}

案例

实体类/VO类

 可以发现UserVo的时间属性与User的时间属性名称不一致基本类型也不一致,这时候就用到了dateFormat()


name:如果源属性为null,会赋上defaultValue中的默认值

/**
 * @Author: 一个肥鲇鱼
 */
@Mapper
public interface BeanConvert {

    BeanConvert COPY_PROPERTIES = Mappers.getMapper(BeanConvert.class);

    /**
     * 将UserVo属性赋值到User
     *
     * 原属性name为null时,赋上默认值(一个肥鲇鱼)
     * 将createTime映射到time,并格式化为yyyy-MM-dd
     *
     * @param vo
     * @return
     */
    @Mappings({
            @Mapping(source = "name", target = "name", defaultValue = "一个肥鲇鱼"),
            @Mapping(source = "createTime", target = "time", dateFormat = "yyyy-MM-dd"),
    })
    User convertUser(UserVo vo);
}

 测试

    @Test
    void test() {
        UserVo vo = new UserVo();
        vo.setId(1);
        vo.setName(null);
        vo.setCreateTime(new Date());
        // 调用转换方法
        User user = BeanConvert.COPY_PROPERTIES.convertUser(vo);

        System.out.println(user);// User{id=1, name='一个肥鲇鱼', time='2023-02-09'}
    }

解释

Mapstruct在编译中执行,会生成一个类似于手写的(settter/getter)类。如下图:

 

 List互转

/**
 * @Author: 一个肥鲇鱼
 */
@Mapper
public interface BeanConvert {

    BeanConvert COPY_PROPERTIES = Mappers.getMapper(BeanConvert.class);

    /**
     * list互转
     *
     * @param voList
     * @return
     */
    @Mappings({
            @Mapping(source = "name", target = "name", defaultValue = "一个肥鲇鱼"),
            @Mapping(source = "createTime", target = "time", dateFormat = "yyyy-MM-dd"),
    })
    List<User> convertUserList(List<UserVo> voList);
}

测试 

    @Test
    void test() {
        List<UserVo> voList = new ArrayList<>();
        for (int i = 0; i <= 2; i++) {
            UserVo vo = new UserVo();
            vo.setId(i + 1);
            vo.setName("一个肥鲇鱼");
            vo.setCreateTime(new Date());
            voList.add(vo);
        }
        // 调用转换方法
        List<User> userList = BeanConvert.COPY_PROPERTIES.convertUserList(voList);

        System.out.println(userList);
        // [User{id=1, name='一个肥鲇鱼', time='2023-02-09'},User{id=2, name='一个肥鲇鱼', time='2023-02-09'}, User{id=3,
        // name='一个肥鲇鱼', time='2023-02-09'}]
    }

编译后的文件

多个源对象映射

再新增一个实体类(UserTo)

/**
 * @Author: 一个肥鲇鱼
 */
@Mapper
public interface BeanConvert {

    BeanConvert COPY_PROPERTIES = Mappers.getMapper(BeanConvert.class);

    /**
     * 将to的id赋给User,将vo的name赋给User
     *
     * @param vo
     * @param to
     * @return
     */
    @Mappings({
            @Mapping(source = "to.id",target = "id"),
            @Mapping(source = "vo.name",target = "name"),
    })
    User convertUserTwo(UserVo vo, UserTo to);
}

    @Test
    void test() {

        UserVo vo = new UserVo();
        vo.setId(1);
        vo.setName("一个肥鲇鱼");

        UserTo to = new UserTo();
        to.setId(111);
        to.setName("张三");
        // 调用转换方法
        User user = BeanConvert.COPY_PROPERTIES.convertUserTwo(vo, to);
        System.out.println(user); // User{id=111, name='一个肥鲇鱼', time='null'}
}

 使用其他值

虽然UserVo有id属性,但映射器会优先使用convertUser方法里面的id参数去映射

/**
 * @Author: 一个肥鲇鱼
 */
@Mapper
public interface BeanConvert {

    BeanConvert COPY_PROPERTIES = Mappers.getMapper(BeanConvert.class);

    @Mapping(source = "id", target = "id")
    User convertUser(UserVo vo, Integer id);
}

自定义方法

工具类

import java.text.SimpleDateFormat;
import java.util.Date;

import org.mapstruct.Named;

/**
 * @Author: 一个肥鲇鱼
 */
public class DateUtils {

	@Named("dateToString")
	public static String dateToString(Date date){
		return date == null ? "": new SimpleDateFormat("yyyy-MM-dd").format(date);
	}

}

/**
 * @Author: 一个肥鲇鱼
 */
@Mapper(uses = {DateUtils.class})
public interface BeanConvert {

    BeanConvert COPY_PROPERTIES = Mappers.getMapper(BeanConvert.class);

    /**
     * 使用自定义方法,处理时间

     * @param vo
     * @return
     */
    @Mapping(source = "createTime",target = "time",qualifiedByName = "dateToString")
    User convertUserTwo(UserVo vo);
}

常见问题

问题一:

No property named “XXX“ exists in source parameter(s). Did you mean “null“?

根本原因:看一下项目的/classes里面,看对应的类是不是编译生成了,或者说类是有的,但是里面没有geter/setter等方法(如果使用Lombok那么主要是Lombok的版本原因)未使用Lombok的,注意实体类中的geter/setter有没有遗漏。

问题二

Couldn’t retrieve @Mapper annotation

两种情况

  1. 包冲突:项目中使用了swagger,swagger里面也包含mapstruct,排除掉就好
  2. 导入mapstruct项目时,同时使用了 mapstruct 和 mapstruct-processor 但是版本不一致,将版本号改为一样即可。
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一个肥鲶鱼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值