java:mapstruct使用

java:mapstruct使用

1 介绍

官网:https://mapstruct.org/,使用版本:1.4.2.Final
java:mapstruct使用_第1张图片
使用场景:pojo之间的相互转化

mapstruct:基于jsr269实现在编译期间生成代码,性能高,精细控制,解耦

orika:能够精细控制,解耦

org.springframework.beans.BeanUtils体系:简单易用,不能对属性进行定制处理

2 mapstruct使用

2.1 不使用框架的缺点

多而杂的代码与业务逻辑耦合,重复劳动等

使用前,依赖配置如下(mapstruct版本为1.4.2.Final,配置相关依赖有:mapstruct和mapstruct-processor):

<properties>
    <mapstruct.version>1.4.2.Finalmapstruct.version>
properties>

<parent>
    <groupId>org.springframework.bootgroupId>
    <artifactId>spring-boot-starter-parentartifactId>
    <version>2.5.4version>
parent>

<dependencies>
    <dependency>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-webartifactId>
    dependency>
    <dependency>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-validationartifactId>
    dependency>
    <dependency>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-testartifactId>
        <scope>testscope>
    dependency>
    <dependency>
        <groupId>org.projectlombokgroupId>
        <artifactId>lombokartifactId>
        <version>1.18.22version>
    dependency>
    <dependency>
        <groupId>junitgroupId>
        <artifactId>junitartifactId>
        <version>4.12version>
        <scope>testscope>
    dependency>
    <dependency>
        <groupId>org.mapstructgroupId>
        <artifactId>mapstructartifactId>
        <version>${mapstruct.version}version>
    dependency>
    <dependency>
        <groupId>org.mapstructgroupId>
        <artifactId>mapstruct-processorartifactId>
        <version>${mapstruct.version}version>
    dependency>
    <dependency>
        <groupId>org.mybatis.spring.bootgroupId>
        <artifactId>mybatis-spring-boot-starterartifactId>
        <version>1.3.2version>
    dependency>
    <dependency>
        <groupId>mysqlgroupId>
        <artifactId>mysql-connector-javaartifactId>
        <scope>runtimescope>
    dependency>
dependencies>

先创建各个实体类:
在这里插入图片描述
在这里插入图片描述
FruitDTO(@Data已包含toString方法,可忽略):

package com.xiaoxu.boot.dto;

import lombok.Data;
import lombok.ToString;

import java.math.BigDecimal;

/**
 * @author xiaoxu
 * @date 2021-12-08
 * spring_boot:com.xiaoxu.boot.dto.FruitDTO
 */
@Data
@ToString
public class FruitDTO {
    /*
     * 水果id
     * */
    private long fId;
    /*
     * 水果市场价格
     * */
    private double fPrice;
    /*
     * 水果实际价格
     * */
    private BigDecimal rPrice;
    /*
     * 水果名称
     * */
    private String fName;
}

ShopDTO:

package com.xiaoxu.boot.dto;

import lombok.Data;
import lombok.ToString;
import java.util.List;

/**
 * @author xiaoxu
 * @date 2021-12-08
 * spring_boot:com.xiaoxu.boot.dto.MallDTO
 */
@Data
@ToString
public class ShopDTO {
    /*
    * 商店id
    * */
    private long sid;
    /*
    * 商店名称
    * */
    private String sname;
    /*
    * 商品内各个品种水果
    * */
    private List<FruitDTO> fruitDTOS;
    /*
    * 商店开张日期
    * */
    private String publishDate;
}

FruitVo:

package com.xiaoxu.boot.vo;

import lombok.Data;
import lombok.ToString;


/**
 * @author xiaoxu
 * @date 2021-12-08
 * spring_boot:com.xiaoxu.boot.vo.FruitVo
 */
@Data
@ToString
public class FruitVo {
    /*
    * 水果id
    * */
    private long fruitId;
    /*
    * FruitVo 水果市场价格:保留两位小数(字符串)
    * */
    private String fruitPrice;
    /*
    * 水果实际价格:保留两位小数(字符串)
    * */
    private String realPrice;
    /*
    * 水果名称
    * */
    private String fruitName;
}

ShopVo:

package com.xiaoxu.boot.vo;

import lombok.Data;

import java.util.Date;
import java.util.List;

/**
 * @author xiaoxu
 * @date 2021-12-08
 * spring_boot:com.xiaoxu.boot.vo.ShopVo
 */
@Data
public class ShopVo {
    /*
     * 商店id
     * */
    private long shopId;
    /*
     * 商店名称
     * */
    private String shopName;
    /*
     * 商品内各个品种水果
     * */
    private List<FruitVo> fruitVos;
    /*
     * 商店开张日期
     * */
    private Date publishDate;
    /*
    * 商店是否存在水果
    * */
    boolean hasFruit;
}

传统使用getter和setter转换实体类:

package com.xiaoxu.convertor;

import com.xiaoxu.boot.dto.FruitDTO;
import com.xiaoxu.boot.dto.ShopDTO;
import com.xiaoxu.boot.vo.FruitVo;
import com.xiaoxu.boot.vo.ShopVo;

import java.math.BigDecimal;
import java.text.DecimalFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * @author xiaoxu
 * @date 2022-02-28
 * spring_boot:com.xiaoxu.convertor.traditionalConvertor
 */
public class traditionalConvertor {
    public static void main(String[] args) throws ParseException {
        ShopDTO shopDTO = traditionalConvertor.ShopDTOBuild();
        System.out.println("shopDto:");
        System.out.println(shopDTO+"\n");
        ShopVo shopVo = traditionalConvertor.ShopVoBuild(shopDTO);
        System.out.println("shopVo:");
        System.out.println(shopVo);
    }
    private static ShopVo ShopVoBuild(ShopDTO shopDTO) throws ParseException {
        //构造商店vo
        ShopVo shopVo = new ShopVo();
        shopVo.setShopId(shopDTO.getSid());
        shopVo.setShopName(shopDTO.getSname());
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        shopVo.setPublishDate(sdf.parse(shopDTO.getPublishDate()));

        List<FruitVo> fruitVos = new ArrayList<>();
        // 构造各个品种水果Vo
        shopDTO.getFruitDTOS().forEach(fruitDTO -> {
            FruitVo fruitVo=new FruitVo();
            fruitVo.setFruitId(fruitDTO.getFId());
            fruitVo.setFruitName(fruitDTO.getFName());
            DecimalFormat decimalFormat = new DecimalFormat("#.00");
            fruitVo.setFruitPrice(decimalFormat.format(fruitDTO.getFPrice()));
            fruitVo.setRealPrice(decimalFormat.format(fruitDTO.getRPrice()));
            fruitVos.add(fruitVo);
        });
        shopVo.setFruitVos(fruitVos);
        shopVo.setHasFruit(shopVo.getFruitVos()!=null&&!shopVo.getFruitVos().isEmpty());
        return shopVo;
    }
    private static ShopDTO ShopDTOBuild(){
        //构造商店Dto
        ShopDTO shopDTO=new ShopDTO();
        shopDTO.setSid(12345L);
        shopDTO.setSname("小天超市");
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        shopDTO.setPublishDate(sdf.format(new Date()));

        // 构造各个品种水果Dto
        //第一个:
        FruitDTO fruitDTO=new FruitDTO();
        fruitDTO.setFId(1);
        fruitDTO.setFPrice(12.5478d);
        fruitDTO.setRPrice(new BigDecimal("10.35"));
        fruitDTO.setFName("荔枝");
        //第二个:
        FruitDTO fruitDTO1 = new FruitDTO();
        fruitDTO1.setFId(2);
        fruitDTO1.setFPrice(19.9999d);
        fruitDTO1.setRPrice(new BigDecimal("15.8"));
        fruitDTO1.setFName("菠萝");
        List<FruitDTO> fruitDTOList=new ArrayList<FruitDTO>(){{
            add(fruitDTO);
            add(fruitDTO1);
        }};

        shopDTO.setFruitDTOS(fruitDTOList);
        return shopDTO;
    }
}

结果如下:

shopDto:
ShopDTO(sid=12345, sname=小天超市, fruitDTOS=[FruitDTO(fId=1, fPrice=12.5478, rPrice=10.35, fName=荔枝), FruitDTO(fId=2, fPrice=19.9999, rPrice=15.8, fName=菠萝)], publishDate=2022-02-28 19:41:05)

shopVo:
ShopVo(shopId=12345, shopName=小天超市, fruitVos=[FruitVo(fruitId=1, fruitPrice=12.55, realPrice=10.35, fruitName=荔枝), FruitVo(fruitId=2, fruitPrice=20.00, realPrice=15.80, fruitName=菠萝)], publishDate=Mon Feb 28 19:41:05 CST 2022, hasFruit=true)

2.2 @Mapper
java:mapstruct使用_第2张图片
BaseConvertor :

package com.xiaoxu.convertor;

import org.mapstruct.Mapper;

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

/**
 * @author xiaoxu
 * @date 2022-02-28
 * spring_boot:com.xiaoxu.convertor.BaseConvertor
 */
@Mapper
public abstract class BaseConvertor {

    Date parseDate(String date){
        try{
            return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(date);
        }catch (ParseException p){
            return null;
        }
    }
}

MapStructConvertor:

package com.xiaoxu.convertor;

import com.xiaoxu.boot.dto.ShopDTO;
import com.xiaoxu.boot.vo.ShopVo;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.factory.Mappers;

/**
 * @author xiaoxu
 * @date 2022-02-28
 * spring_boot:com.xiaoxu.convertor.MapStructConvertor
 */
//可以接口或者抽象类,该接口或者类上增加注解@Mapper
@Mapper
public abstract class MapStructConvertor extends BaseConvertor{
    public static final MapStructConvertor INSTANCE= Mappers.getMapper(MapStructConvertor.class);
    /*
    * shopDTO->shopVo
    * */
    @Mapping(target = "publishDate",expression = "java(parseDate(shopDTO.getPublishDate()))")
    public abstract ShopVo shopDto2shopVo(ShopDTO shopDTO);
}

MyConvertor:

package com.xiaoxu.convertor;

import com.xiaoxu.boot.dto.ShopDTO;
import com.xiaoxu.boot.vo.ShopVo;

/**
 * @author xiaoxu
 * @date 2022-02-28
 * spring_boot:com.xiaoxu.convertor.MyConvertor
 */
public class MyConvertor {
    public static void main(String[] args) {
        ShopDTO shopDTO = traditionalConvertor.ShopDTOBuild();
        System.out.println("shopDto:");
        System.out.println(shopDTO+"\n");

        ShopVo shopVo = MapStructConvertor.INSTANCE.shopDto2shopVo(shopDTO);
        System.out.println("使用mapstruct后的shopVo:");
        System.out.println(shopVo);
    }
}

修改traditionalConvertor后:

package com.xiaoxu.convertor;

import com.xiaoxu.boot.dto.FruitDTO;
import com.xiaoxu.boot.dto.ShopDTO;
import com.xiaoxu.boot.vo.FruitVo;
import com.xiaoxu.boot.vo.ShopVo;

import java.math.BigDecimal;
import java.text.DecimalFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * @author xiaoxu
 * @date 2022-02-28
 * spring_boot:com.xiaoxu.convertor.traditionalConvertor
 */
public class traditionalConvertor {
    public static void main(String[] args) throws ParseException {
        ShopDTO shopDTO = traditionalConvertor.ShopDTOBuild();
        System.out.println("shopDto:");
        System.out.println(shopDTO+"\n");
        ShopVo shopVo = traditionalConvertor.ShopVoBuild(shopDTO);
        System.out.println("shopVo:");
        System.out.println(shopVo);
    }
    public static ShopVo ShopVoBuild(ShopDTO shopDTO) throws ParseException {
        //构造商店vo
        ShopVo shopVo = new ShopVo();
        shopVo.setShopId(shopDTO.getSid());
        shopVo.setShopName(shopDTO.getSname());
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        shopVo.setPublishDate(sdf.parse(shopDTO.getPublishDate()));

        List<FruitVo> fruitVos = new ArrayList<>();
        // 构造各个品种水果Vo
        shopDTO.getFruitDTOS().forEach(fruitDTO -> {
            FruitVo fruitVo=new FruitVo();
            fruitVo.setFruitId(fruitDTO.getFId());
            fruitVo.setFruitName(fruitDTO.getFName());
            DecimalFormat decimalFormat = new DecimalFormat("#.00");
            fruitVo.setFruitPrice(decimalFormat.format(fruitDTO.getFPrice()));
            fruitVo.setRealPrice(decimalFormat.format(fruitDTO.getRPrice()));
            fruitVos.add(fruitVo);
        });
        shopVo.setFruitVos(fruitVos);
        shopVo.setHasFruit(shopVo.getFruitVos()!=null&&!shopVo.getFruitVos().isEmpty());
        return shopVo;
    }
    public static ShopDTO ShopDTOBuild(){
        //构造商店Dto
        ShopDTO shopDTO=new ShopDTO();
        shopDTO.setSid(12345L);
        shopDTO.setSname("小天超市");
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        shopDTO.setPublishDate(sdf.format(new Date()));

        // 构造各个品种水果Dto
        //第一个:
        FruitDTO fruitDTO=new FruitDTO();
        fruitDTO.setFId(1);
        fruitDTO.setFPrice(12.5478d);
        fruitDTO.setRPrice(new BigDecimal("10.35"));
        fruitDTO.setFName("荔枝");
        //第二个:
        FruitDTO fruitDTO1 = new FruitDTO();
        fruitDTO1.setFId(2);
        fruitDTO1.setFPrice(19.9999d);
        fruitDTO1.setRPrice(new BigDecimal("15.8"));
        fruitDTO1.setFName("菠萝");
        List<FruitDTO> fruitDTOList=new ArrayList<FruitDTO>(){{
            add(fruitDTO);
            add(fruitDTO1);
        }};

        shopDTO.setFruitDTOS(fruitDTOList);
        return shopDTO;
    }
}

执行MyConvertor结果如下:

shopDto:
ShopDTO(sid=12345, sname=小天超市, fruitDTOS=[FruitDTO(fId=1, fPrice=12.5478, rPrice=10.35, fName=荔枝), FruitDTO(fId=2, fPrice=19.9999, rPrice=15.8, fName=菠萝)], publishDate=2022-02-28 20:39:08)

使用mapstruct后的shopVo:
ShopVo(shopId=0, shopName=null, fruitVos=null, publishDate=Mon Feb 28 20:39:08 CST 2022, hasFruit=false)

2.3 @Mappings和@Mapping

指定属性之间的映射关系

默认映射规则:(1)同类型且同名的属性,会自动映射;(2)mapstruct会自动进行类型转换:1、8种基本类型和对应的包装类型之间会自动转换;2、8种基本类型(包含对应的包装类型),和String之间;3、日期类型和String之间

上述可知,有些字段默认是0,false或者null,因为字段名称不一致,默认mapstruct是不会进行映射的,故而需要增加字段映射:

修改MapStructConvertor,重新执行MyConvertor:

package com.xiaoxu.convertor;

import com.xiaoxu.boot.dto.ShopDTO;
import com.xiaoxu.boot.vo.ShopVo;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.factory.Mappers;

/**
 * @author xiaoxu
 * @date 2022-02-28
 * spring_boot:com.xiaoxu.convertor.MapStructConvertor
 */
//可以接口或者抽象类,该接口或者类上增加注解@Mapper
@Mapper
public abstract class MapStructConvertor extends BaseConvertor{
    public static final MapStructConvertor INSTANCE= Mappers.getMapper(MapStructConvertor.class);
    /*
    * shopDTO->shopVo
    * */
    @Mapping(source = "sid",target = "shopId")
    @Mapping(source = "sname",target = "shopName")
    @Mapping(source = "fruitDTOS",target = "fruitVos")
    @Mapping(target = "publishDate",expression = "java(parseDate(shopDTO.getPublishDate()))")
    public abstract ShopVo shopDto2shopVo(ShopDTO shopDTO);
}

结果如下:

shopDto:
ShopDTO(sid=12345, sname=小天超市, fruitDTOS=[FruitDTO(fId=1, fPrice=12.5478, rPrice=10.35, fName=荔枝), FruitDTO(fId=2, fPrice=19.9999, rPrice=15.8, fName=菠萝)], publishDate=2022-02-28 20:42:53)

使用mapstruct后的shopVo:
ShopVo(shopId=12345, shopName=小天超市, fruitVos=[FruitVo(fruitId=0, fruitPrice=null, realPrice=null, fruitName=null), FruitVo(fruitId=0, fruitPrice=null, realPrice=null, fruitName=null)], publishDate=Mon Feb 28 20:42:53 CST 2022, hasFruit=false)

上述的多个@Mapping,可以装在@Mappings中,效果一致,修改如下:

package com.xiaoxu.convertor;

import com.xiaoxu.boot.dto.ShopDTO;
import com.xiaoxu.boot.vo.ShopVo;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.Mappings;
import org.mapstruct.factory.Mappers;

/**
 * @author xiaoxu
 * @date 2022-02-28
 * spring_boot:com.xiaoxu.convertor.MapStructConvertor
 */
//可以接口或者抽象类,该接口或者类上增加注解@Mapper
@Mapper
public abstract class MapStructConvertor extends BaseConvertor{
    public static final MapStructConvertor INSTANCE= Mappers.getMapper(MapStructConvertor.class);
    /*
    * shopDTO->shopVo
    * */
    @Mappings(value = {
            @Mapping(source = "sid",target = "shopId"),
            @Mapping(source = "sname",target = "shopName"),
            @Mapping(source = "fruitDTOS",target = "fruitVos"),
            @Mapping(target = "publishDate",expression = "java(parseDate(shopDTO.getPublishDate()))")
    })
    public abstract ShopVo shopDto2shopVo(ShopDTO shopDTO);
}

且@Mapping的source和target参数中存在不存在的成员变量时,有提示:
java:mapstruct使用_第3张图片
java:mapstruct使用_第4张图片
日期类型和String之间可以默认转换,还可以加上日期格式化参数:
修改如下:

package com.xiaoxu.convertor;

import com.xiaoxu.boot.dto.ShopDTO;
import com.xiaoxu.boot.vo.ShopVo;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.Mappings;
import org.mapstruct.factory.Mappers;

/**
 * @author xiaoxu
 * @date 2022-02-28
 * spring_boot:com.xiaoxu.convertor.MapStructConvertor
 */
//可以接口或者抽象类,该接口或者类上增加注解@Mapper
@Mapper
public abstract class MapStructConvertor extends BaseConvertor{
    public static final MapStructConvertor INSTANCE= Mappers.getMapper(MapStructConvertor.class);
    /*
    * shopDTO->shopVo
    * */
    @Mappings(value = {
            @Mapping(source = "sid",target = "shopId"),
            @Mapping(source = "sname",target = "shopName"),
            @Mapping(source = "fruitDTOS",target = "fruitVos"),
            @Mapping(target = "publishDate",source = "publishDate",dateFormat = "yyyy-MM-dd HH:mm:ss")
    })
    public abstract ShopVo shopDto2shopVo(ShopDTO shopDTO);
}

执行结果:

shopDto:
ShopDTO(sid=12345, sname=小天超市, fruitDTOS=[FruitDTO(fId=1, fPrice=12.5478, rPrice=10.35, fName=荔枝), FruitDTO(fId=2, fPrice=19.9999, rPrice=15.8, fName=菠萝)], publishDate=2022-03-01 13:25:27)

使用mapstruct后的shopVo:
ShopVo(shopId=12345, shopName=小天超市, fruitVos=[FruitVo(fruitId=0, fruitPrice=null, realPrice=null, fruitName=null), FruitVo(fruitId=0, fruitPrice=null, realPrice=null, fruitName=null)], publishDate=Tue Mar 01 13:25:27 CST 2022, hasFruit=false)

使用数字格式化前,先对商店的水果List进行映射,在mapstruct中,对引用类型对象进行映射时,只需要在@Mapper修饰的抽象类或接口中,增加对应的抽象方法即可,如下:

修改如下:

package com.xiaoxu.convertor;

import com.xiaoxu.boot.dto.FruitDTO;
import com.xiaoxu.boot.dto.ShopDTO;
import com.xiaoxu.boot.vo.FruitVo;
import com.xiaoxu.boot.vo.ShopVo;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.Mappings;
import org.mapstruct.factory.Mappers;

import java.util.List;

/**
 * @author xiaoxu
 * @date 2022-02-28
 * spring_boot:com.xiaoxu.convertor.MapStructConvertor
 */
//可以接口或者抽象类,该接口或者类上增加注解@Mapper
@Mapper
public abstract class MapStructConvertor extends BaseConvertor{
    public static final MapStructConvertor INSTANCE= Mappers.getMapper(MapStructConvertor.class);
    /*
    * shopDTO->shopVo
    * */
    @Mappings(value = {
            @Mapping(source = "sid",target = "shopId"),
            @Mapping(source = "sname",target = "shopName"),
            @Mapping(source = "fruitDTOS",target = "fruitVos"),
            @Mapping(source = "publishDate",target = "publishDate",dateFormat = "yyyy-MM-dd HH:mm:ss")
    })
    public abstract ShopVo shopDto2shopVo(ShopDTO shopDTO);

    @Mappings(value = {
            @Mapping(source = "FId",target = "fruitId"),
            @Mapping(source = "FPrice",target = "fruitPrice"),
            @Mapping(source = "RPrice",target = "realPrice"),
            @Mapping(source = "FName",target = "fruitName")
    })
    public abstract FruitVo fruitDto2FruitVo(FruitDTO fruitDTO);

    public abstract List<FruitVo> fruitDtos2FruitVos(List<FruitDTO> fruitDTO);
}

执行MyConvertor结果如下:

shopDto:
ShopDTO(sid=12345, sname=小天超市, fruitDTOS=[FruitDTO(fId=1, fPrice=12.5478, rPrice=10.35, fName=荔枝), FruitDTO(fId=2, fPrice=19.9999, rPrice=15.8, fName=菠萝)], publishDate=2022-03-01 14:05:13)

使用mapstruct后的shopVo:
ShopVo(shopId=12345, shopName=小天超市, fruitVos=[FruitVo(fruitId=1, fruitPrice=12.5478, realPrice=10.35, fruitName=荔枝), FruitVo(fruitId=2, fruitPrice=19.9999, realPrice=15.8, fruitName=菠萝)], publishDate=Tue Mar 01 14:05:13 CST 2022, hasFruit=false

由此可知,List之间的对应,不要用@Mapping进行映射,只需要将引用对象之间的抽象方法对应(含字段的Mapping映射),再加上list之间对应的抽象方法即可。如果是单独的引用对象之间的映射,也是定义引用对象的映射抽象方法即可。

数字格式化修改:
java:mapstruct使用_第5张图片
执行结果如下:

shopDto:
ShopDTO(sid=12345, sname=小天超市, fruitDTOS=[FruitDTO(fId=1, fPrice=12.5478, rPrice=10.35, fName=荔枝), FruitDTO(fId=2, fPrice=19.9999, rPrice=15.8, fName=菠萝)], publishDate=2022-03-01 14:05:59)

使用mapstruct后的shopVo:
ShopVo(shopId=12345, shopName=小天超市, fruitVos=[FruitVo(fruitId=1, fruitPrice=12.55, realPrice=10.35, fruitName=荔枝), FruitVo(fruitId=2, fruitPrice=20.00, realPrice=15.8, fruitName=菠萝)], publishDate=Tue Mar 01 14:05:59 CST 2022, hasFruit=false)

因为list制定了Mapping映射,所以下面的List映射抽象方法,去掉不影响最终的结果:
java:mapstruct使用_第6张图片
如果有不需要映射转换的字段,那么@Mapping增加ignore参数即可,修改如下:

package com.xiaoxu.convertor;

import com.xiaoxu.boot.dto.FruitDTO;
import com.xiaoxu.boot.dto.ShopDTO;
import com.xiaoxu.boot.vo.FruitVo;
import com.xiaoxu.boot.vo.ShopVo;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.Mappings;
import org.mapstruct.factory.Mappers;

import java.util.List;

/**
 * @author xiaoxu
 * @date 2022-02-28
 * spring_boot:com.xiaoxu.convertor.MapStructConvertor
 */
//可以接口或者抽象类,该接口或者类上增加注解@Mapper
@Mapper
public abstract class MapStructConvertor extends BaseConvertor{
    public static final MapStructConvertor INSTANCE= Mappers.getMapper(MapStructConvertor.class);
    /*
    * shopDTO->shopVo
    * */
    @Mappings(value = {
            @Mapping(source = "sid",target = "shopId"),
            @Mapping(source = "sname",target = "shopName"),
            @Mapping(source = "fruitDTOS",target = "fruitVos"),
            @Mapping(source = "publishDate",target = "publishDate",dateFormat = "yyyy-MM-dd HH:mm:ss"),
//            @Mapping(target = "publishDate",expression = "java(parseDate(shopDTO.getPublishDate()))")
    })
    public abstract ShopVo shopDto2shopVo(ShopDTO shopDTO);

    @Mappings(value = {
            @Mapping(source = "FId",target = "fruitId",ignore = true),
            @Mapping(source = "FPrice",target = "fruitPrice",numberFormat = "#.00"),
            @Mapping(source = "RPrice",target = "realPrice"),
            @Mapping(source = "FName",target = "fruitName")
    })
    public abstract FruitVo fruitDto2FruitVo(FruitDTO fruitDTO);

//    public abstract List fruitDtos2FruitVos(List fruitDTO);
}

执行结果:

shopDto:
ShopDTO(sid=12345, sname=小天超市, fruitDTOS=[FruitDTO(fId=1, fPrice=12.5478, rPrice=10.35, fName=荔枝), FruitDTO(fId=2, fPrice=19.9999, rPrice=15.8, fName=菠萝)], publishDate=2022-03-01 19:16:42)

使用mapstruct后的shopVo:
ShopVo(shopId=12345, shopName=小天超市, fruitVos=[FruitVo(fruitId=0, fruitPrice=12.55, realPrice=10.35, fruitName=荔枝), FruitVo(fruitId=0, fruitPrice=20.00, realPrice=15.8, fruitName=菠萝)], publishDate=Tue Mar 01 19:16:42 CST 2022, hasFruit=false)

2.4 @AfterMapping和@MappingTarget

在映射最后一步,对属性的自定义映射处理

package com.xiaoxu.convertor;

import com.xiaoxu.boot.dto.FruitDTO;
import com.xiaoxu.boot.dto.ShopDTO;
import com.xiaoxu.boot.vo.FruitVo;
import com.xiaoxu.boot.vo.ShopVo;
import org.mapstruct.*;
import org.mapstruct.factory.Mappers;

import java.util.List;

/**
 * @author xiaoxu
 * @date 2022-02-28
 * spring_boot:com.xiaoxu.convertor.MapStructConvertor
 */
//可以接口或者抽象类,该接口或者类上增加注解@Mapper
@Mapper
public abstract class MapStructConvertor extends BaseConvertor{
    public static final MapStructConvertor INSTANCE= Mappers.getMapper(MapStructConvertor.class);
    /*
    * shopDTO->shopVo
    * */
    @Mappings(value = {
            @Mapping(source = "sid",target = "shopId"),
            @Mapping(source = "sname",target = "shopName"),
            @Mapping(source = "fruitDTOS",target = "fruitVos"),
            @Mapping(source = "publishDate",target = "publishDate",dateFormat = "yyyy-MM-dd HH:mm:ss"),
//            @Mapping(target = "publishDate",expression = "java(parseDate(shopDTO.getPublishDate()))")
    })
    public abstract ShopVo shopDto2shopVo(ShopDTO shopDTO);

    @Mappings(value = {
            @Mapping(source = "FId",target = "fruitId",ignore = true),
            @Mapping(source = "FPrice",target = "fruitPrice",numberFormat = "#.00"),
            @Mapping(source = "RPrice",target = "realPrice"),
            @Mapping(source = "FName",target = "fruitName")
    })
    public abstract FruitVo fruitDto2FruitVo(FruitDTO fruitDTO);

    @AfterMapping
    public void doAfter(ShopDTO shopDTO, @MappingTarget ShopVo shopVo){
        List<FruitDTO> fruitDTOS = shopDTO.getFruitDTOS();
        shopVo.setHasFruit(fruitDTOS!=null&&!fruitDTOS.isEmpty());
    }

//    public abstract List fruitDtos2FruitVos(List fruitDTO);
}

执行结果如下:

shopDto:
ShopDTO(sid=12345, sname=小天超市, fruitDTOS=[FruitDTO(fId=1, fPrice=12.5478, rPrice=10.35, fName=荔枝), FruitDTO(fId=2, fPrice=19.9999, rPrice=15.8, fName=菠萝)], publishDate=2022-03-01 19:33:13)

使用mapstruct后的shopVo:
ShopVo(shopId=12345, shopName=小天超市, fruitVos=[FruitVo(fruitId=0, fruitPrice=12.55, realPrice=10.35, fruitName=荔枝), FruitVo(fruitId=0, fruitPrice=20.00, realPrice=15.8, fruitName=菠萝)], publishDate=Tue Mar 01 19:33:13 CST 2022, hasFruit=true)

2.5 @BeanMapping

ignoreByDefault:忽略mapstruct的默认映射行为。避免不需要的赋值、避免属性覆盖

package com.xiaoxu.convertor;

import com.xiaoxu.boot.dto.FruitDTO;
import com.xiaoxu.boot.dto.ShopDTO;
import com.xiaoxu.boot.vo.FruitVo;
import com.xiaoxu.boot.vo.ShopVo;
import org.mapstruct.*;
import org.mapstruct.factory.Mappers;

import java.util.List;

/**
 * @author xiaoxu
 * @date 2022-02-28
 * spring_boot:com.xiaoxu.convertor.MapStructConvertor
 */
//可以接口或者抽象类,该接口或者类上增加注解@Mapper
@Mapper
public abstract class MapStructConvertor extends BaseConvertor{
    public static final MapStructConvertor INSTANCE= Mappers.getMapper(MapStructConvertor.class);
    /*
    * shopDTO->shopVo
    * */
    @Mappings(value = {
            @Mapping(source = "sid",target = "shopId"),
//            @Mapping(source = "sname",target = "shopName"),
//            @Mapping(source = "fruitDTOS",target = "fruitVos"),
//            @Mapping(source = "publishDate",target = "publishDate",dateFormat = "yyyy-MM-dd HH:mm:ss"),
//            @Mapping(target = "publishDate",expression = "java(parseDate(shopDTO.getPublishDate()))")
    })
    @BeanMapping(ignoreByDefault = true)
    public abstract ShopVo shopDto2shopVo(ShopDTO shopDTO);

    @Mappings(value = {
            @Mapping(source = "FId",target = "fruitId",ignore = true),
            @Mapping(source = "FPrice",target = "fruitPrice",numberFormat = "#.00"),
            @Mapping(source = "RPrice",target = "realPrice"),
            @Mapping(source = "FName",target = "fruitName")
    })
    public abstract FruitVo fruitDto2FruitVo(FruitDTO fruitDTO);

    @AfterMapping
    public void doAfter(ShopDTO shopDTO, @MappingTarget ShopVo shopVo){
        List<FruitDTO> fruitDTOS = shopDTO.getFruitDTOS();
        shopVo.setHasFruit(fruitDTOS!=null&&!fruitDTOS.isEmpty());
    }

//    public abstract List fruitDtos2FruitVos(List fruitDTO);
}

执行结果如下:
可见,默认映射规则失效,只有@Mapping(source = “sid”,target = “shopId”),和doAfter生效:

shopDto:
ShopDTO(sid=12345, sname=小天超市, fruitDTOS=[FruitDTO(fId=1, fPrice=12.5478, rPrice=10.35, fName=荔枝), FruitDTO(fId=2, fPrice=19.9999, rPrice=15.8, fName=菠萝)], publishDate=2022-03-01 19:39:53)

使用mapstruct后的shopVo:
ShopVo(shopId=12345, shopName=null, fruitVos=null, publishDate=null, hasFruit=true)

2.6 结合spring

@Mapper(componentModel = “spring”)本质上,就是增加@Component注解。

代码如下:

package com.xiaoxu.convertor;

import com.xiaoxu.boot.dto.FruitDTO;
import com.xiaoxu.boot.dto.ShopDTO;
import com.xiaoxu.boot.vo.FruitVo;
import com.xiaoxu.boot.vo.ShopVo;
import org.mapstruct.*;
import org.mapstruct.factory.Mappers;

import java.util.List;

/**
 * @author xiaoxu
 * @date 2022-02-28
 * spring_boot:com.xiaoxu.convertor.MapStructConvertor
 */
//可以接口或者抽象类,该接口或者类上增加注解@Mapper
@Mapper(componentModel = "spring")
public abstract class MapStructConvertor extends BaseConvertor{
    public static final MapStructConvertor INSTANCE= Mappers.getMapper(MapStructConvertor.class);
    /*
    * shopDTO->shopVo
    * */
    @Mappings(value = {
            @Mapping(source = "sid",target = "shopId"),
//            @Mapping(source = "sname",target = "shopName"),
//            @Mapping(source = "fruitDTOS",target = "fruitVos"),
//            @Mapping(source = "publishDate",target = "publishDate",dateFormat = "yyyy-MM-dd HH:mm:ss"),
//            @Mapping(target = "publishDate",expression = "java(parseDate(shopDTO.getPublishDate()))")
    })
    @BeanMapping(ignoreByDefault = true)
    public abstract ShopVo shopDto2shopVo(ShopDTO shopDTO);

    @Mappings(value = {
            @Mapping(source = "FId",target = "fruitId",ignore = true),
            @Mapping(source = "FPrice",target = "fruitPrice",numberFormat = "#.00"),
            @Mapping(source = "RPrice",target = "realPrice"),
            @Mapping(source = "FName",target = "fruitName")
    })
    public abstract FruitVo fruitDto2FruitVo(FruitDTO fruitDTO);

    @AfterMapping
    public void doAfter(ShopDTO shopDTO, @MappingTarget ShopVo shopVo){
        List<FruitDTO> fruitDTOS = shopDTO.getFruitDTOS();
        shopVo.setHasFruit(fruitDTOS!=null&&!fruitDTOS.isEmpty());
    }

//    public abstract List fruitDtos2FruitVos(List fruitDTO);
}

单测类如下:

import com.xiaoxu.boot.MainApplication;
import com.xiaoxu.boot.dto.ShopDTO;
import com.xiaoxu.boot.vo.ShopVo;
import com.xiaoxu.convertor.MapStructConvertor;
import com.xiaoxu.convertor.traditionalConvertor;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

/**
 * @author xiaoxu
 * @date 2022-03-01
 * spring_boot:PACKAGE_NAME.TestMapSct
 */
@RunWith(SpringRunner.class)
@SpringBootTest(classes = MainApplication.class)
public class TestMapSct {
    @Autowired
    MapStructConvertor mapStructConvertor;

    @Test
    public void test_01(){
        ShopDTO shopDTO = traditionalConvertor.ShopDTOBuild();

        ShopVo shopVo = mapStructConvertor.shopDto2shopVo(shopDTO);
        System.out.println(shopVo);
    }
}

结果如下:

ShopVo(shopId=12345, shopName=null, fruitVos=null, publishDate=null, hasFruit=true)

注意:这里主程序类和MyConvertor不在同个包路径下,执行SpringBoot的单测时,需要给启动类增加扫描路径:
java:mapstruct使用_第7张图片
java:mapstruct使用_第8张图片
如此单测才可顺利运行。

你可能感兴趣的:(Java,java,spring,boot,spring)