MapStruct 基本使用,优雅的构建对象,对象转换

MapStruct使用指南

在SpringBoot项目开发中经常会涉及到各种对象的构建,代码中可能会充斥各种对象构建的代码,有没有更加优雅的解决方案呢

代码实战

直接上例子
接口接收参数时经常涉及到请求参数对象到Bo对象的转换

// 请求参数对象如下
@Data
public class CustomRequest{

    @ApiModelProperty(value = "param1", required = true)
    private String param1;

    @ApiModelProperty(value = "param2", required = true)
    private String param2;
}
// Bo对象如下
@Data
public class CustomBo{

    @ApiModelProperty(value = "param1", required = true)
    private String param1;

    @ApiModelProperty(value = "param2", required = true)
    private String param2;

	@ApiModelProperty(value = "flag", required = true)
    private boolean flag;
}

// controller层接口代码为
@RestController
@Slf4j
@RequiredArgsConstructor
@RequestMapping(value = "/public/v1")
public class BusinessController implements BusinessApi {

    private final BusinessService service;

    @Override
    @PostMapping(value = "/detail")
    public Result<Vo> detail(@Valid @RequestBody CustomRequest request) {
    	// 构建对象的代码使得代码风格变得混乱
    	CustomBo bo = new CustomBo()
    	bo.setParam1(request.getParam1());
    	bo.setParam2(request.getParam2());
    	bo.setFlag(bo.isFlag());
        return ResultUtils.success(service.detail(bo));
    }
}

使用Mapstruct优化后只需要一行代码解决问题

@RestController
@Slf4j
@RequiredArgsConstructor
@RequestMapping(value = "/public/v1")
public class BusinessController implements BusinessApi {

    private final BusinessService service;

    @Override
    @PostMapping(value = "/detail")
    public Result<Vo> detail(@Valid @RequestBody CustomRequest request) {
    	// 只需要一行即可解决,对象属性越多效果越明显
        CustomBo bo = CustomConverter.INSTANCE.buildBo(param, true);
        return ResultUtils.success(service.detail(bo));
    }
}

需要额外增加一个接口,指定对象转换规则

import org.mapstruct.Mapper;
import org.mapstruct.Mapping;

@Mapper
public interface CustomConverter {

    CustomConverter INSTANCE = Mappers.getMapper(CustomConverter.class);
    
    /**
     * Request转换Bo对象
     *
     * @param param1         请求参数1
     * @param param2         请求参数2
     * @param flag           flag
     * @return bo对象
     */
    @Mapping(target = "param1", source = "request.param1")
    @Mapping(target = "param2", source = "request.param2")
    @Mapping(target = "flag", source = "flag")
    CustomBo buildBo(CustomRequest request, boolean flag);
}

框架介绍

MapStruct是一个Java注解处理器,主要用于自动生成Bean映射代码。
核心功能:

  • 自动生成Bean映射代码:MapStruct可以在编译时生成源对象和目标对象之间的映射代码,无需手动编写繁琐的转换逻辑。
  • 高性能:MapStruct生成的代码使用纯Java方法调用,避免了反射或类似机制,从而提高了性能。

优势:

  • 简化开发:MapStruct可以自动处理Java Bean之间的映射,从而简化了开发过程,节省了时间和精力。
  • 减少错误:由于MapStruct生成的代码是类型安全的,因此可以在编译时捕获到映射不完整或映射不正确等错误。
  • 提高性能:与基于反射的映射框架相比,MapStruct生成的代码具有更高的性能。

常用功能:

  • 自定义映射:MapStruct支持自定义映射规则,包括字段名不同、类型转换等场景。
  • 集合映射:MapStruct可以轻松处理集合之间的映射,包括List、Set等。
  • 枚举映射:支持枚举类型之间的映射。
  • 嵌套属性映射:可以处理具有嵌套属性的Java Bean之间的映射

使用指南

基础使用

先导入依赖

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

创建一个对象转换接口

import org.mapstruct.Mapper;
import org.mapstruct.Mapping;

@Mapper
public interface CustomConverter {

    CustomConverter INSTANCE = Mappers.getMapper(CustomConverter.class);
    
    /**
     * Request转换Bo对象
     *
     * @param param1         请求参数1
     * @param param2         请求参数2
     * @param flag           flag
     * @return bo对象
     */
    @Mapping(target = "param1", source = "request.param1")
    @Mapping(target = "param2", source = "request.param2")
    @Mapping(target = "flag", source = "flag")
    CustomBo buildBo(CustomRequest request, boolean flag);
}
// @Mapping 用来指定属性映射规则 source指定属性值来源,target指定映射到返回对象的哪一个属性

使用时直接这样用就行

CustomBo bo = CustomConverter.INSTANCE.buildBo(param, true);

构建数据库实体

其实,我们可以发散思维,MapStruct可以使用到各种需要构建对象的场景
例如构建数据库实体对象插入DB的时候那就有一个问题需要解决,构建数据库对象实体是时涉及到Date对象的实例化,而不只是属性的赋值,MapStruct支持吗?没问题,是支持的!
数据库实体对象定义如下

/**
 * 订单表
 *
 * @TableName order
 */
@TableName(value = "order")
@Data
public class Order implements Serializable {
    /**
     * 自增主键
     */
    @TableId
    private Long id;

    /**
     * 品牌
     */
    private String brand;

    /**
     * 业务类型 例:MUSIC_VIP
     */
    private String category;

    /**
     * 状态:INIT 初始,AUTH:授权 FINISH:部分转交易剩余解冻 CLOSE:全解冻或授权超时
     */
    private String status;

    /**
     * 交易金额
     */
    private BigDecimal payAmount;

    /**
     * 创建时间
     */
    private Date createTime;

    /**
     * 更新时间
     */
    private Date updateTime;

    /**
     * 失效时间
     */
    private Date expireTime;

    private static final long serialVersionUID = 1L;

}
import com.test.enums.OrderStatusEnum;
import java.math.BigDecimal;
import java.util.Date;


@Mapper(imports = {OrderStatusEnum.class, BigDecimal.class, Date.class})
public interface OrderConvert {

    /**
     * 构建AuthOrder
     *
     * @param request    请求体
     * @param resp 支付宝预授权响应信息
     * @return response
     */
    @Mapping(target = "category", source = "request.category")
    @Mapping(target = "status", expression = "java(OrderStatusEnum.INIT.toString())")
    @Mapping(target = "payAmount", expression = "java(new BigDecimal(0))")
    @Mapping(target = "createTime", expression = "java(new Date())")
    @Mapping(target = "updateTime", expression = "java(new Date())")
    @Mapping(target = "expireTime", expression = "java(new Date())")
    Order buildOrder(OrderCreateReq request);
}
// 使用时只需要这样即可
// 先引入这个组件
private final OrderConvert orderConvert;

// 使用时一行代码搞定
Order order = OrderConvert.buildOrder(request);

在构建对象过程中,要执行java代码例如实例化对象等,需要注意的点如下

  1. Converter接口上需要加使用Mapper注解的imports属性,指定需要引入的类
    @Mapper(imports = {OrderStatusEnum.class, BigDecimal.class, Date.class})
    同时需要在import部分手动把需要的类引入

  2. 在编写涉及到执行java代码的属性映射规则时需要使用Mapping注解的expression属性
    @Mapping(target = “expireTime”, expression = “java(new Date())”)
    写法是这样expression = "java(待执行的代码)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值