MapStruct应用指南

目录

1、MapStruct是什么

2、MapStruct的使用

2.1、引入依赖

2.2、使用方式

2.2.1、工厂类方式

2.2.2、Spring 注入方式

2.3、转换方式

2.3.1、属性名称相同,直接转换

2.3.2、属性名不相同, 可通过 @Mapping 注解进行指定转化

2.3.3、Mapper 中使用自定义的转换

2.4、多个对象转一个对象

2.5、多层对象转换(嵌套对象)

2.6、缺省值(默认值)和常量

2.7、使用java表达式进行映射

3、MapStruct的注解说明

4、MapStruct 相关资料


1、MapStruct是什么

MapStruct 是一个属性映射工具,只需要定义一个 Mapper 接口,MapStruct 就会自动实现这个映射接口,避免了复杂繁琐的映射实现。

实际编码中会涉及到多种对象,po、vo、dto、entity、do、domain这些定义的对象运用在不同的场景模块中,这种对象与对象之间的互相转换,就需要有一个专门用来解决转换问题的工具。

以前是通过反射的方法实现,但是现在无论是 BeanUtils, BeanCopier 等在使用反射的时候都会影响到性能,再后来自己写转换器一个个赋值,但是很浪费时间, 而且在添加新的字段的时候也要进行方法的修改。

MapStruct的目标是通过尽可能地自动化来简化这项工作。

与其他映射框架不同,MapStruct在编译时生成bean映射,这确保了高性能,允许快速的开发人员反馈和彻底的错误检查。

2、MapStruct的使用

2.1、引入依赖

<properties>
    <!--项目编码格式-->
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <!--java版本号-->
    <java.version>1.8</java.version>
    <!--mapstruct对象属性映射框架-->
    <org.mapstruct.version>1.4.2.Final</org.mapstruct.version>
    <!--对象属性简化框架-->
    <org.projectlombok.version>1.18.16</org.projectlombok.version>
</properties>
<dependencies>
    <!--包含一些注解-->
    <dependency>
        <groupId>org.mapstruct</groupId>
        <artifactId>mapstruct</artifactId>
        <version>${org.mapstruct.version}</version>
    </dependency>
    <!--注解处理器,根据注解自动生成mapper的实现-->
    <dependency>
        <groupId>org.mapstruct</groupId>
        <artifactId>mapstruct-processor</artifactId>
        <version>${org.mapstruct.version}</version>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>${org.projectlombok.version}</version>
        <scope>provided</scope>
    </dependency>
</dependencies>

2.2、使用方式

2.2.1、工厂类方式

@Mapper
public interface OrderItemMapper {
    OrderItemMapper MAPPER = Mappers.getMapper(OrderItemMapper.class);
    /**
     * OrderItemDto 转为 OrderItem
     *
     * @param orderItemDto 订单DTO对象
     * @return OrderItem
     * @author kaifeng
     * @date 2021/2/21 5:06 下午
     */
    OrderItem dto2OrderItem(OrderItemDto orderItemDto);
}

使用方式,调用工厂类:
OrderItemMapper.MAPPER.dto2OrderItem(new OrderItemDto());

2.2.2、Spring 注入方式

@Mapper(componentModel = "spring")
public interface OrderItemMapper {

//    OrderItemMapper MAPPER = Mappers.getMapper(OrderItemMapper.class);

    /**
     * OrderItemDto 转为 OrderItem
     *
     * @param orderItemDto 订单DTO对象
     * @return OrderItem
     * @author kaifeng
     * @date 2021/2/21 5:06 下午
     */
    OrderItem dto2OrderItem(OrderItemDto orderItemDto);
}
使用方式,对象注入:
@Autowired
private OrderItemMapper orderItemMapper;

2.3、转换方式

2.3.1、属性名称相同,直接转换

类似于BeanUtils转换的方式,按照属性名称匹配进行赋值

/**
 * 产品信息对象
 *
 * @author : liukaifeng
 * @date : 2021/2/28 10:10 上午
 */
@Data
public class Product {
    private String productId;
    private String name;
}
/**
 * 产品信息DTO对象
 *
 * @author : liukaifeng
 * @date : 2021/2/28 10:11 上午
 */
@Data
public class ProductDTO implements Serializable {
    private String productId;
    private String name;
}
/**
 * 产品对象转换
 *
 * @author : liukaifeng
 * @date : 2021/2/28 10:12 上午
 */
public interface ProductMapper {
    ProductDTO toDTO(Product product);
}

2.3.2、属性名不相同, 可通过 @Mapping 注解进行指定转化

/**
 * 产品信息对象
 *
 * @author : liukaifeng
 * @date : 2021/2/28 10:10 上午
 */
@Data
public class Product {
    private String productId;
    private String name;
}
/**
 * 产品信息DTO对象
 *
 * @author : liukaifeng
 * @date : 2021/2/28 10:11 上午
 */
@Data
public class ProductDTO implements Serializable {
    private String productId;
    private String productName;
}
/**
 * 产品对象转换
 *
 * @author : liukaifeng
 * @date : 2021/2/28 10:12 上午
 */
public interface ProductMapper {
    @Mapping(source = "name", target = "productName")
    ProductDTO toDTO(Product product);
}

2.3.3、Mapper 中使用自定义的转换

对于某些类型, 无法按照属性名称直接转换,就需要自定义的方法来进行转换。

2.3.3.1、日期处理

/**
 * 产品信息对象
 *
 * @author : liukaifeng
 * @date : 2021/2/28 10:10 上午
 */
@Data
public class Product {
    private String productId;
    private String name;
    private BigDecimal price;
    /**
     * 生产时间
     */
    private Date productionTime;
    /**
     * 过期时间
     */
    private String expirationTime;
}
/**
 * 产品信息DTO对象
 *
 * @author : liukaifeng
 * @date : 2021/2/28 10:11 上午
 */
@Data
public class ProductDTO implements Serializable {
    /**
     * 产品id
     */
    private String productId;
    /**
     * 产品名称
     */
    private String productName;
    /**
     * 产品规格
     */
    private String specs;
    /**
     * 产品描述
     */
    private String describe;
    /**
     * 产品价格
     */
    private String price;
    /**
     * 生产时间
     */
    private String productionTime;
    /**
     * 过期时间
     */
    private Date expirationTime;
}
@Test
public void test2DTODate() {
    Product product = new Product();
    product.setProductId("1");
    product.setName("小汽车");

    product.setProductionTime(new Date());
    product.setExpirationTime("2021-02-28 11:22:30");
    ProductDTO productDTO = ProductMapper.MAPPER.toDTODate(product);
    System.out.println(productDTO);
}

2.3.3.2、金额处理

/**
 * 产品信息对象
 *
 * @author : liukaifeng
 * @date : 2021/2/28 10:10 上午
 */
@Data
public class Product {
    private String productId;
    private String name;
    private BigDecimal price;
}
/**
 * 产品信息DTO对象
 *
 * @author : liukaifeng
 * @date : 2021/2/28 10:11 上午
 */
@Data
public class ProductDTO implements Serializable {
    /**
     * 产品id
     */
    private String productId;
    /**
     * 产品名称
     */
    private String productName;
    /**
     * 产品规格
     */
    private String specs;
    /**
     * 产品描述
     */
    private String describe;
    /**
     * 产品价格
     */
    private String price;
}
/**
 * 产品对象转换
 *
 * @author : liukaifeng
 * @date : 2021/2/28 10:12 上午
 */
//@Mapper(componentModel = "spring")
public interface ProductMapper {
    ProductMapper MAPPER = Mappers.getMapper(ProductMapper.class);

    /**
     * BigDecimal转换成字符串
     *
     * @param product 产品基础信息对象
     * @return : ProductDTO
     * @author : liukaifeng
     * @date : 2021/2/28 10:25 上午
     */
    @Mapping(target = "productId", source = "product.productId")
    @Mapping(source = "name", target = "productName")
    @Mapping(target = "price", numberFormat = "#.00元")
    ProductDTO toPriceDTO(Product product);
}

2.3.3.3、自定义转换器

MapStruct支持自定义转换器,实现类型之间自定义规则的转换。

一个自定义映射器可以定义多个映射方法,匹配时,是以方法的入参和出参进行匹配。如果绑定的映射中,存在多个相同的入参和出参方法,将会报错。

如果多个入参或者出参方法存在继承关系,将会匹配最具体的那一个方法。

/**
 * 产品对象
 * 用于自定义转换器测试
 * @author : liukaifeng
 * @date : 2021/2/28 12:04 下午
 */
@Data
public class CProduct {
    private String productId;
    private List<String> images;
}
/**
 * 产品DTO对象
 * 用于自定义转换器测试
 *
 * @author : liukaifeng
 * @date : 2021/2/28 12:04 下午
 */
@Data
public class CProductDTO implements Serializable {
    private static final long serialVersionUID = 2184784038009791692L;
    private String productId;
    private String images;
}
/**
 * 图片对象转换器
 *
 * @author : liukaifeng
 * @date : 2021/2/28 12:06 下午
 */
public class ImageFormater {
    public String format(List<String> images) {
        return String.join(",", images);
    }
}
/**
 * 自定义转换器验证
 *
 * @author : liukaifeng
 * @date : 2021/2/28 11:45 上午
 */
//@Mapper(componentModel = "spring", uses = ImageFormater.class)
@Mapper(uses = ImageFormater.class)
public interface CProductMapper {
    CProductMapper MAPPER = Mappers.getMapper(CProductMapper.class);

    CProductDTO toDTO(CProduct product);
}

2.4、多个对象转一个对象

/**
 * 产品信息对象
 *
 * @author : liukaifeng
 * @date : 2021/2/28 10:10 上午
 */
@Data
public class Product {
    private String productId;
    private String name;
}

/**
 * 产品详情
 *
 * @author : liukaifeng
 * @date : 2021/2/28 10:21 上午
 */
@Data
public class ProductDetail {

    /**
     * 产品规格
     */
    private String specs;
    /**
     * 产品描述
     */
    private String describe;
}
/**
 * 产品信息DTO对象
 *
 * @author : liukaifeng
 * @date : 2021/2/28 10:11 上午
 */
@Data
public class ProductDTO implements Serializable {
    /**
     * 产品id
     */
    private String productId;
    /**
     * 产品名称
     */
    private String productName;
    /**
     * 产品规格
     */
    private String specs;
    /**
     * 产品描述
     */
    private String describe;
}
/**
 * 产品对象转换
 *
 * @author : liukaifeng
 * @date : 2021/2/28 10:12 上午
 */
//@Mapper(componentModel = "spring")
public interface ProductMapper {
    ProductMapper MAPPER = Mappers.getMapper(ProductMapper.class);

    /**
     * 转为产品DTO
     * 个别字段名称不一致,使用 @Mapping 指定映射关系
     *
     * @param product 产品基础信息对象
     * @return : ProductDTO
     * @author : liukaifeng
     * @date : 2021/2/28 10:25 上午
     */
    @Mapping(source = "name", target = "productName")
    ProductDTO toDTO(Product product);


    /**
     * 多对象转为一个产品DTO对象
     *
     * @param product 产品基础信息对象
     * @return : ProductDTO
     * @author : liukaifeng
     * @date : 2021/2/28 10:25 上午
     */
    @Mapping(target = "productId", source = "product.productId")
    @Mapping(source = "name", target = "productName")
    @Mapping(target = "specs", source = "detail.specs")
    @Mapping(target = "desc", source = "detail.desc")
    ProductDTO toDetailDTO(Product product, ProductDetail detail);
}

2.5、多层对象转换(嵌套对象)

/**
 * 产品对象,内嵌产品详情对象
 *
 * @author : liukaifeng
 * @date : 2021/2/28 11:40 上午
 */
@Data
public class NestProduct {
    private String productId;
    private NestProductDetail nestProductDetail;
}
/**
 * 产品详情对象
 *
 * @author : liukaifeng
 * @date : 2021/2/28 11:41 上午
 */
@Data
public class NestProductDetail {
    private String id;
}
/**
 * 产品DTO对象
 *
 * @author : liukaifeng
 * @date : 2021/2/28 11:42 上午
 */
@Data
public class NestProductDTO implements Serializable {
    private static final long serialVersionUID = 2184784038009791692L;
    private String productId;
    private NestProductDetailDTO nestProductDetailDTO;
}
/**
 * 产品DTO详情对象
 *
 * @author : liukaifeng
 * @date : 2021/2/28 11:43 上午
 */
@Data
public class NestProductDetailDTO implements Serializable {
    private String productId;
    private String detailId;
}

@Test
public void test2NestProductDTO() {
    NestProductDetail nestProductDetail = new NestProductDetail();
    nestProductDetail.setId("100");
    NestProduct product = new NestProduct();
    product.setProductId("1");
    product.setNestProductDetail(nestProductDetail);


    NestProductDTO nestProductDTO = NestProductMapper.MAPPER.toDTO(product);
    System.out.println(nestProductDTO);
}

2.6、缺省值(默认值)和常量

MapStruct允许设置缺省值和常量,同时缺省值允许使用表达式。

注意:使用缺省值,源字段必须存在,否则缺省值不生效,否则应该使用常量。

/**
 * 产品信息对象
 *
 * @author : liukaifeng
 * @date : 2021/2/28 10:10 上午
 */
@Data
public class Product {
    private String productId;
    private String name;
    private BigDecimal price;
    /**
     * 生产时间
     */
    private Date productionTime;
    /**
     * 过期时间
     */
    private String expirationTime;
    /**
     * 随机数
     */
    private String random;
}
/**
 * 产品信息DTO对象
 *
 * @author : liukaifeng
 * @date : 2021/2/28 10:11 上午
 */
@Data
public class ProductDTO implements Serializable {
    /**
     * 产品id
     */
    private String productId;
    /**
     * 产品名称
     */
    private String productName;
    /**
     * 产品规格
     */
    private String specs;
    /**
     * 产品描述
     */
    private String describe;
    /**
     * 产品价格
     */
    private String price;
    /**
     * 生产时间
     */
    private String productionTime;
    /**
     * 过期时间
     */
    private Date expirationTime;
    /**
     * 随机数
     */
    private String random;
}
/**
 * 缺省值验证
 *
 * @author : liukaifeng
 * @date : 2021/2/28 2:12 下午
 */
//@Mapper(componentModel = "spring", imports = UUID.class)
@Mapper(imports = UUID.class)
public interface DefaultValueMapper {
    DefaultValueMapper MAPPER = Mappers.getMapper(DefaultValueMapper.class);

    @Mapping(target = "productId", source = "productId", defaultValue = "0") //当product的productId为null,设置为0
    @Mapping(target = "random", source = "random", defaultExpression = "java(UUID.randomUUID().toString())") //缺省设置随机数
    @Mapping(target = "productionTime", dateFormat = "yyyy-MM-dd", constant = "2021-02-28") //默认设置为2021-02-28
    ProductDTO toDTO(Product product);
}
@Test
public void testProductDTODefaultValue() {
    ProductDTO productDTO = DefaultValueMapper.MAPPER.toDTO(new Product());
    System.out.printf(productDTO.toString());
}

2.7、使用java表达式进行映射

@Mapper(imports = DecimalUtils.class) //导入java表达式使用的类
public interface Demo16Assembler {
    @Mapping(target = "price", expression = "java(product.getPrice1() + product.getPrice2())") //字段相加
    @Mapping(target = "price2", expression = "java(DecimalUtils.add(product.getPrice1(), product.getPrice2()))")//使用工具类处理
    ProductDTO toDTO(Product product);
}

3、MapStruct的注解说明

@Mapper 只有在接口加上这个注解, MapStruct 才会去实现该接口
    @Mapper 里有个 componentModel 属性,主要是指定实现类的类型,有如下4种方式
    default: 这是默认的情况,MapStruct不使用任何组件类型, 可以通过Mappers.getMapper(Class)方式获取自动生成的实例对象。
    cdi: the generated mapper is an application-scoped CDI bean and can be retrieved via @Inject。
    spring: 生成的实现类上面会自动添加一个@Component注解,可以通过Spring的 @Autowired方式进行注入。
    jsr330: 生成的实现类上会添加@javax.inject.Named 和@Singleton注解,可以通过 @Inject注解获取。

@Mapping:属性映射,若源对象属性与目标对象名字一致,会自动映射对应属性
    source:源属性
    target:目标属性
    dateFormat:String 到 Date 日期之间相互转换,通过 SimpleDateFormat,该值为 SimpleDateFormat 的日期格式
    numberFormat:数值格式化, 例:"0.00"
    expression: 自定义java代码实现属性映射
    ignore: 忽略这个字段
@Mappings:配置多个@Mapping
@MappingTarget 用于更新已有对象
@InheritConfiguration 用于继承配置

4、MapStruct 相关资料

1、MapStruct 1.3.0.Final参考指南

MapStruct 1.3.0.Final参考指南

2、5种常见Bean映射工具的性能比对

https://www.baeldung.com/java-performance-mapping-frameworks

3、官方文档:

https://mapstruct.org/documentation/stable/reference/html/

4、官方FAQ:

https://mapstruct.org/faq/

5、官方Example:

https://github.com/mapstruct/mapstruct-examples

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java MapStruct一个代码生成器,用于处理Java bean之间的映射。它通过在编译时生成映射代码来提高性能,并且可以自定义映射逻辑。以下是使用Java MapStruct的步骤: 1. 添加MapStruct依赖项到Maven或Gradle项目中。 2. 创建一个Java接口,该接口定义了要映射的源和目标bean之间的映射方法。 3. 在接口上使用@Mapper注释,指定MapStruct生成的实现类的名称。 4. 在映射方法上使用@Mapping注释,指定源和目标bean属性之间的映射关系。 5. 在Maven或Gradle项目中运行编译命令,以生成MapStruct实现类。 6. 在代码中使用MapStruct生成的实现类来执行bean之间的映射。 下面是一个使用Java MapStruct的简单示例: 1. 添加MapStruct依赖项到Maven或Gradle项目中。 ```xml <dependency> <groupId>org.mapstruct</groupId> <artifactId>mapstruct-jdk8</artifactId> <version>1.4.2.Final</version> </dependency> ``` 2. 创建一个Java接口,该接口定义了要映射的源和目标bean之间的映射方法。 ```java @Mapper public interface CarMapper { CarDto carToCarDto(Car car); } ``` 3. 在接口上使用@Mapper注释,指定MapStruct生成的实现类的名称。 ```java @Mapper(componentModel = "spring") public interface CarMapper { CarDto carToCarDto(Car car); } ``` 4. 在映射方法上使用@Mapping注释,指定源和目标bean属性之间的映射关系。 ```java @Mapper(componentModel = "spring") public interface CarMapper { @Mapping(source = "numberOfSeats", target = "seatCount") CarDto carToCarDto(Car car); } ``` 5. 在Maven或Gradle项目中运行编译命令,以生成MapStruct实现类。 6. 在代码中使用MapStruct生成的实现类来执行bean之间的映射。 ```java @Autowired private CarMapper carMapper; public void example() { Car car = new Car("Morris", 5); CarDto carDto = carMapper.carToCarDto(car); } ```

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值