实体类转换工具 MapStruct,不要再使用BeanUtils了

目录

1、性能分析

2、什么是Mapstruct

4、普通使用方法

4.1 创建DO

4.2创建DTO

4.3创建一个基础的Mapstruct类

4.4创建映射器接口

5、spring依赖注入使用方法

6、总结

7、源码和官网


1、性能分析

在我们平时开发过程中,往往会涉及到,对象和对象直接的转化,而看到这个需求时直接想到的就是反射和BeanUtils工具,但是这个之间的效率往往是非常低的。

/**
 * @author Bill
 * @date Created in 2021/9/28 15:59 <br>
 */
public class ReflectTest {

    /**
     * 实体转换次数
     */
    private static final int LIMIT_NUM = 1;

    /**
     * 反射性能分析
     */
    @Test
    public void beanUtil() {
        long start = System.currentTimeMillis();

        OrderDTO orderDto = new OrderDTO();
        orderDto.setName("mapstruct 测试");
        orderDto.setCost(12L);
        orderDto.setPrice("13");
        orderDto.setWeight(123L);
        orderDto.setIsDeleted(false);
        orderDto.setIsFinish(true);
        orderDto.setGmtCreat(LocalDateTime.now());
        orderDto.setGmtModified(LocalDateTime.now());

        ItemDTO itemDTO = new ItemDTO();
        itemDTO.setItemName("衬衫");
        itemDTO.setPrice(12L);
        orderDto.setItemList(Arrays.asList(itemDTO));


        for (int i = 0; i < LIMIT_NUM; i++) {
            final Order order = new Order();
            BeanUtils.copyProperties(orderDto, order);
        }

        long end = System.currentTimeMillis();
        System.out.println("BeanUtils耗时:"+(end - start ) + "ms");

        mapstruct();
    }

    /**
     * 反射性能分析
     */
    @Test
    public void mapstruct() {
        long start = System.currentTimeMillis();

        OrderDTO orderDto = new OrderDTO();
        orderDto.setName("mapstruct 测试");
        orderDto.setCost(12L);
        orderDto.setPrice("13");
        orderDto.setWeight(123L);
        orderDto.setIsDeleted(false);
        orderDto.setIsFinish(true);
        orderDto.setGmtCreat(LocalDateTime.now());
        orderDto.setGmtModified(LocalDateTime.now());

        ItemDTO itemDTO = new ItemDTO();
        itemDTO.setItemName("衬衫");
        itemDTO.setPrice(12L);
        orderDto.setItemList(Arrays.asList(itemDTO));

        for (int i = 0; i < LIMIT_NUM; i++) {
            final Order order = new Order();
            OrderMapper.INSTANCE.updateOrder(orderDto, order);
        }

        long end = System.currentTimeMillis();
        System.out.println("mapstruct耗时:"+(end - start ) + "ms");
    }
}



平均结果:

BeanUtils耗时:150ms
mapstruct耗时:10ms

从上面可以看出BeanUtils和Mapstruct的性能差距还是蛮大的。

当然很多人也会说放射如果通过缓存性能能极大的提升,但是问题是,大部分的情况下我们是无法满足缓存的条件,而且这种反射往往比较麻烦,所以今天来看看Mapstruct这个技术。

2、什么是Mapstruct

原文

MapStruct is a code generator that greatly simplifies the implementation of mappings between Java bean types based on a convention over configuration approach.

The generated mapping code uses plain method invocations and thus is fast, type-safe and easy to understand.

Mapsturct 是一个通过配置公约的一种代码生成器,它大大简化了Java Bean类型之间的映射的实现,而且类型安全,性能高,使用简单。

3、准备工作

引入依赖,java11环境时

    <properties>
        <maven.compiler.source>11</maven.compiler.source>
        <maven.compiler.target>11</maven.compiler.target>
        <mapstruct.version>1.3.1.Final</mapstruct.version>
        <maven.compiler.version>3.6.1</maven.compiler.version>
    </properties>        


        <dependency>
            <groupId>org.glassfish.jaxb</groupId>
            <artifactId>jaxb-runtime</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
        <dependency>
            <groupId>org.mapstruct</groupId>
            <artifactId>mapstruct</artifactId>
            <version>${mapstruct.version}</version>
        </dependency>
        <dependency>
            <groupId>org.mapstruct</groupId>
            <artifactId>mapstruct-processor</artifactId>
            <version>${mapstruct.version}</version>
            <scope>provided</scope>
        </dependency>

4、普通使用方法

4.1 创建DO

package com.eagle.example.mapstruct.dataobject;

import lombok.Data;

import java.io.Serializable;
import java.time.LocalDateTime;
import java.util.List;

/**
 * 订单实体类
 *
 * @author Bill
 * @date Created in 2021/9/23 14:35 <br>
 */
@Data
public class Order implements Serializable {
    /**
     * 商品标题
     */
    private String title;
    /**
     * 商品价格
     */
    private Long price;
    /**
     * 订单的重量
     */
    private Long weight;
    /**
     * 订单成本
     */
    private Long cost;
    /**
     * 宝贝列表
     */
    List<Item> itemList;
    /**
     * 是否被删除
     */
    private String isDeleted;
    /**
     * 是否完成订单
     */
    private Boolean isFinish;
    /**
     * 创建时间
     */
    private String gmtCreat;
    /**
     * 更新时间
     */
    private LocalDateTime gmtModified;
}
package com.eagle.example.mapstruct.dataobject;

import lombok.Data;

/**
 * 宝贝信息
 *
 * @author Bill
 * @date Created in 2021/9/26 15:28 <br>
 */
@Data
public class Item {
    private String itemName;
    private Long price;
}

4.2创建DTO

package com.eagle.example.mapstruct.dto;

import lombok.Data;

import java.io.Serializable;
import java.time.LocalDateTime;
import java.util.List;

/**
 * @author Bill
 * @date Created in 2021/9/23 14:40 <br>
 */
@Data
public class OrderDTO implements Serializable {
    /**
     * 商品标题
     */
    private String name;
    /**
     * 商品价格
     */
    private String price;
    /**
     * 订单的重量
     */
    private Long weight;
    /**
     * 订单成本
     */
    private Long cost;
    /**
     * 宝贝列表
     */
    private List<ItemDTO> itemList;
    /**
     * 是否被删除
     */
    private Boolean isDeleted;
    /**
     * 是否完成订单
     */
    private Boolean isFinish;
    /**
     * 创建时间
     */
    private LocalDateTime gmtCreat;
    /**
     * 更新时间
     */
    private LocalDateTime gmtModified;
}
package com.eagle.example.mapstruct.dto;

import lombok.Data;

/**
 * 宝贝信息do
 *
 * @author Bill
 * @date Created in 2021/9/26 15:29 <br>
 */
@Data
public class ItemDTO {
    private String itemName;
    private Long price;
}
package com.eagle.example.mapstruct.dto;

import lombok.Data;

import java.io.Serializable;

/**
 * @author Bill
 * @date Created in 2021/9/23 14:40 <br>
 */
@Data
public class OrderInfoDTO implements Serializable {

    /**
     * 商品标题
     */
    private String name;
    /**
     * 商品价格
     */
    private String price;

}

4.3创建一个基础的Mapstruct类

可以编写一些基础方法和通用功能,因为随着Localdate的引入DO也都使用了Localdate类型,但是Mapstruct对Localdate支持比较弱,所以引入

package com.eagle.example.mapstruct.convert;

/**
 * @author Bill
 * @date Created in 2021/9/26 17:39 <br>
 */

import org.mapstruct.Mapper;

import java.time.LocalDate;
import java.time.LocalDateTime;

@Mapper(imports = {LocalDateTime.class, LocalDate.class})
public interface BaseMapstruct {
}

4.4创建映射器接口

当前例子是比较全的一种形态,所以很多不需要使用的自己酌情删除

package com.eagle.example.mapstruct.convert;

/**
 * @author Bill
 * @date Created in 2021/9/26 17:08 <br>
 */
public class BooleanStrFormat {

    private static final String Y_CHAR = "Y";

    private static final String N_CHAR = "N";

    public String toStr(Boolean isDisable) {
        if (isDisable) {
            return Y_CHAR;
        } else {
            return N_CHAR;
        }
    }

    public Boolean toBoolean(String str) {
        if (str.equals(Y_CHAR)) {
            return true;
        } else {
            return false;
        }
    }
}
package com.eagle.example.mapstruct.convert;

import com.eagle.example.mapstruct.dataobject.Order;
import com.eagle.example.mapstruct.dto.OrderDTO;
import com.eagle.example.mapstruct.dto.OrderInfoDTO;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.MappingTarget;
import org.mapstruct.Mappings;
import org.mapstruct.ap.internal.util.Strings;
import org.mapstruct.factory.Mappers;

import java.util.List;

/**
 * @author Bill
 * @date Created in 2021/9/23 16:43 <br>
 */
@Mapper(uses = BooleanStrFormat.class)
public interface OrderMapper extends BaseMapstruct {

    OrderMapper INSTANCE = Mappers.getMapper(OrderMapper.class);

    String Y_CHAR = "Y";

    String N_CHAR = "N";

    /**
     * 1、属性名不一致转换
     * 2、LocalDateTime 转 string 
     * 3、qualifiedByName 方式Long 转String
     * 4、List 对象转换
     *
     * @param orderDTO
     * @return
     */
    @Mappings({
            @Mapping(source = "name", target = "title"),
            @Mapping(target = "gmtCreat", dateFormat = "yyyy-MM-dd HH:mm:ss"),
            @Mapping(target = "price", qualifiedByName = "toLong")
    })
    Order orderDto2Order(OrderDTO orderDTO);

    /**
     * Order to orderDto
     *
     * @param order
     * @return
     */
    @Mappings({
            @Mapping(source = "title", target = "name"),
            @Mapping(target = "gmtCreat", dateFormat = "yyyy-MM-dd HH:mm:ss"),
            @Mapping(target = "price", qualifiedByName = "toStr")
    })
    OrderDTO order2OrderDto(Order order);

    default Long toLong(String price) {
        if (Strings.isEmpty(price)) {
            return 0L;
        } else {
            return Long.parseLong(price);
        }
    }

    default String toStr(Long price) {
        if (price != null) {
            return price.toString();
        } else {
            return "0";
        }
    }


    /**
     * List对象转换方式
     *
     * @param orderDTOList
     * @return
     */
    List<Order> orderDto2Order(List<OrderDTO> orderDTOList);

    /**
     * 更新订单信息
     *
     * @param orderDTO
     * @return
     */
    @Mappings({
            @Mapping(source = "name", target = "title"),
            @Mapping(target = "gmtCreat", dateFormat = "yyyy-MM-dd HH:mm:ss"),
            @Mapping(target = "price", qualifiedByName = "toLong")
    })
    void updateOrder(OrderDTO orderDTO, @MappingTarget Order order);

    /**
     * 两个对象最终转化成一个对象
     *
     * @param orderDto
     * @param orderInfoDTO
     * @return
     */
    @Mappings({
            @Mapping(source = "orderInfoDTO.name", target = "title"),
            @Mapping(source = "orderDto.gmtCreat", target = "gmtCreat", dateFormat = "yyyy-MM-dd HH:mm:ss"),
            @Mapping(source = "orderInfoDTO.price", target = "price", qualifiedByName = "toLong")
    })
    Order getUserByOrderDTOAndOrderInfoDTO(OrderDTO orderDto, OrderInfoDTO orderInfoDTO);

    /**
     * 两个对象最终转化成一个对象
     *
     * @param orderDto
     * @param orderInfoDTO
     * @return
     */
    @Mappings({
            @Mapping(source = "orderInfoDTO.name", target = "title"),
            @Mapping(source = "orderDto.gmtCreat", target = "gmtCreat", dateFormat = "yyyy-MM-dd HH:mm:ss"),
            @Mapping(target = "price", source = "orderDto.price")
    })
    Order getUserByOrderDTOAndOrderInfoDTOChoicePrice(OrderDTO orderDto, OrderInfoDTO orderInfoDTO);
}

5、spring依赖注入使用方法

package com.eagle.example.mapstruct.convert;

import org.springframework.stereotype.Component;

/**
 * 有需要使用boolean转String方式的转化器
 * @author Bill
 * @date Created in 2021/9/26 17:08 <br>
 */
@Component
public class SpringBooleanStrFormat {

    private static final String Y_CHAR = "Y";

    private static final String N_CHAR = "N";

    public String toStr(Boolean isDisable) {
        if (isDisable) {
            return Y_CHAR;
        } else {
            return N_CHAR;
        }
    }

    public Boolean toBoolean(String str) {
        if (str.equals(Y_CHAR)) {
            return true;
        } else {
            return false;
        }
    }
}
package com.eagle.example.mapstruct.convert;

import com.eagle.example.mapstruct.dataobject.Order;
import com.eagle.example.mapstruct.dto.OrderDTO;
import org.mapstruct.InjectionStrategy;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.Mappings;
import org.mapstruct.ap.internal.util.Strings;

import java.util.List;

/**
 * @author Bill
 * @date Created in 2021/9/23 16:43 <br>
 */
@Mapper(componentModel = "spring", injectionStrategy = InjectionStrategy.CONSTRUCTOR, uses = SpringBooleanStrFormat.class)
public interface SpringOrderMapper extends BaseMapstruct {

    /**
     * orderDto to Order
     *
     * @param orderDTO
     * @return
     */
    @Mappings({
            @Mapping(source = "name", target = "title"),
            @Mapping(target = "gmtCreat", dateFormat = "yyyy-MM-dd HH:mm:ss"),
            @Mapping(target = "price", qualifiedByName = "toLong")
    })
    Order orderDto2Order(OrderDTO orderDTO);

    /**
     * Order to orderDto
     *
     * @param order
     * @return
     */
    @Mappings({
            @Mapping(source = "title", target = "name"),
            @Mapping(target = "gmtCreat", dateFormat = "yyyy-MM-dd HH:mm:ss"),
            @Mapping(target = "price", qualifiedByName = "toStr")
    })
    OrderDTO order2OrderDto(Order order);

    default Long toLong(String price) {
        if (Strings.isEmpty(price)) {
            return 0L;
        } else {
            return Long.parseLong(price);
        }
    }

    default String toStr(Long price) {
        if (price != null) {
            return price.toString();
        } else {
            return "0";
        }
    }


    /**
     * orderDto to Order by list
     *
     * @param orderDTOList
     * @return
     */
    List<Order> orderDto2Order(List<OrderDTO> orderDTOList);

}

6、总结

7、源码和官网

github:eagle-example: 项目例子集合

官网:MapStruct – Java bean mappings, the easy way!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值