MapStruct使用笔记

前言

我们在开发中,经常会遇到实体之间的转换问题。今天给大家带来的是如何优雅的使用mapstruct来完成实体之间的转换。

实体转换方法

手动转换

对实体的数据逐个设置,工作量比较大。

BeanUtils

直接使用spring自带的BeanUtilscopyProperties方法。会有一些性能问题

MapStruct

最近项目在使用Mapstruct,简单记录一下。

作用

主要用于 Java 中实体对象之间的转换,如(VODTOPO…)

VO:用于接收前端请求参数的实体,可以扩展继承DTO或者直接使用DTO。
DTO:用于后端传输数据的实体。
PO:用于orm映射处理的实体,与表结构对应。

只需要编写一个接口,在接口上增加注解,就能够在编译期自动生成接口的实现类,实现类帮我们进行值的set操作。

官网介绍

MapStruct 是一个 Java注释处理器,用于生成类型安全的 bean 映射类。您所要做的就是定义一个映射器接口,它声明任何所需的映射方法。在编译期间,MapStruct 将生成该接口的实现。此实现使用普通 Java 方法调用来在源对象和目标对象之间进行映射,即没有反射或类似的情况。

优点

通过使用普通方法调用而不是反射来快速执行 编译时类型安全:只能映射相互映射的对象和属性,不会意外地将订单实体映射到客户 DTO 等。
在构建时清除错误报告,如果 映射不完整(并非所有目标属性都已映射) 映射不正确(找不到正确的映射方法或类型转换)

如何使用Mapstruct

官方地址

mapstruct官方使用文档

依赖引入

<properties>    	
	<org.mapstruct.version>1.5.5.Final</org.mapstruct.version>
</properties>

<dependency>
	<groupId>org.mapstruct</groupId>
    <artifactId>mapstruct</artifactId>
    <version>${org.mapstruct.version}</version>
</dependency>
<!-- 添加mapstruct插件-->
<plugin>
	<groupId>org.apache.maven.plugins</groupId>
	<artifactId>maven-compiler-plugin</artifactId>
    <version>3.8.1</version>
    <configuration>
    <source>1.8</source>
       <target>1.8</target>
        <annotationProcessorPaths>
            <path>
                <groupId>org.mapstruct</groupId>
                <artifactId>mapstruct-processor</artifactId>
                <version>${org.mapstruct.version}</version>
            </path>
        </annotationProcessorPaths>
    </configuration>
</plugin>

编写接口

当两个转换实体字段和类型一直时

当你要转换的两个对象实体字段名称和类型都一样时,只需要加一个@Mapper注解即可

// 此Mapper注解为mapstruct包下的,不是ibatis中,请注意区分。
@Mapper
public interface DishConvertMapper {
    DishConvertMapper INSTANCE = Mappers.getMapper(DishConvertMapper.class);

    /***
     * 将entity转换为Dto
     * @param dish
     * @return
     */
    DishDto convert(Dish dish);

    /***
     * 将dto转换为entity
     * @param dishDto
     * @return
     */
    Dish convert(DishDto dishDto);
}

当两个转换实体字段不同时

假设我的实体A的字段为name,实体B的字段为userName,实际上存储的数据是一样的,这时,我们需要做一个映射。

 @Mappings(
            {@Mapping(source = "name", target = "userName")}
    )
    DishDto convert(Dish dish);

当有多个字段需要做映射时,只需要再添加一个@Mapping注解即可。

数据类型转换

当两个字段的数据类型不一样时,Mapstruct支持source和target属性的类型转换。提供最基本的数据类型和包装类之间的转换。

  • 基本类型及对应的包装类之间的转换:int 和Integer、long和Long等。
  • 所有基本类型及包装类与String之间都可以自动转换
  • 枚举和String之间
  • 更多类型转换详见官方文档

如何使用

直接使用

原来的写法
//            DishDto dishDto = new DishDto();
//            BeanUtils.copyProperties(dish, dishDto);
//使用mapstruct后的写法
            DishDto dishDto = DishConvertMapper.INSTANCE.convert(dish);

依赖注入

@Mapper(componentModel = "spring")
public interface DishConvertMapper {
  
    /***
     * 将entity转换为Dto
     * @param dish
     * @return
     */
    @Mappings(
            {@Mapping(source = "name", target = "userName")}
    )
    DishDto convert(Dish dish);

    /***
     * 将dto转换为entity
     * @param dishDto
     * @return
     */
    Dish convert(DishDto dishDto);
}

通过@Mapper指定componentModel 为"spring",就会被spring管理,就不需要Mappers.getMapper()的方式,直接通过@Autoware的方式注入。

实现原理

在程序编译时,会自动生成实体转换接口的实现类,实现类在classes路径下。以下是自动生成的实现类:

@Component
public class DishConvertMapperImpl implements DishConvertMapper {
    public DishConvertMapperImpl() {
    }

    public DishDto convert(Dish dish) {
        if (dish == null) {
            return null;
        } else {
            DishDto dishDto = new DishDto();
            dishDto.setId(dish.getId());
            dishDto.setName(dish.getName());
            dishDto.setCategoryId(dish.getCategoryId());
            dishDto.setPrice(dish.getPrice());
            dishDto.setCode(dish.getCode());
            dishDto.setImage(dish.getImage());
            dishDto.setDescription(dish.getDescription());
            dishDto.setStatus(dish.getStatus());
            dishDto.setSort(dish.getSort());
            dishDto.setCreateTime(dish.getCreateTime());
            dishDto.setUpdateTime(dish.getUpdateTime());
            dishDto.setCreateUser(dish.getCreateUser());
            dishDto.setUpdateUser(dish.getUpdateUser());
            return dishDto;
        }
    }

    public Dish convert(DishDto dishDto) {
        if (dishDto == null) {
            return null;
        } else {
            Dish dish = new Dish();
            dish.setId(dishDto.getId());
            dish.setName(dishDto.getName());
            dish.setCategoryId(dishDto.getCategoryId());
            dish.setPrice(dishDto.getPrice());
            dish.setCode(dishDto.getCode());
            dish.setImage(dishDto.getImage());
            dish.setDescription(dishDto.getDescription());
            dish.setStatus(dishDto.getStatus());
            dish.setSort(dishDto.getSort());
            dish.setCreateTime(dishDto.getCreateTime());
            dish.setUpdateTime(dishDto.getUpdateTime());
            dish.setCreateUser(dishDto.getCreateUser());
            dish.setUpdateUser(dishDto.getUpdateUser());
            return dish;
        }
    }
}

可以看到,实现类帮我们做了数据源类的get和目标类的set操作。

遇到问题

java: 找不到符号 符号: 变量 log

这个报错位置是:lombok的@Slf4j注解的log变量

引入MapStruct和Lombok后,构建过程中发生冲突。

解决办法:在pom文件中增加lombok注解支持

<plugin>
   <groupId>org.apache.maven.plugins</groupId>
   <artifactId>maven-compiler-plugin</artifactId>
   <version>3.8.1</version>
   <configuration>
       <source>1.8</source>
       <target>1.8</target>
       <annotationProcessorPaths>
<!--增加lombok注解支持 解决找不到log 符号问题-->
           <path>
               <groupId>org.projectlombok</groupId>
               <artifactId>lombok</artifactId>
               <version>1.18.22</version>
           </path>
           <path>
               <groupId>org.mapstruct</groupId>
               <artifactId>mapstruct-processor</artifactId>
               <version>${org.mapstruct.version}</version>
           </path>
       </annotationProcessorPaths>
   </configuration>
</plugin>

总结

本文只是一个简单的Mapstruct入门使用,如果您使用的场景更复杂,建议您通过官方文档来实现您的需求。

  • 9
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值