DO转DTO映射MapStruct生产环境使用

    技术2022-07-10  142

    什么是MapStruct?

    MapStruct是用于生成类型安全的bean映射类的Java注解处理器。

    你所要做的就是定义一个映射器接口,声明任何需要映射的方法。在编译过程中,MapStruct将生成该接口的实现。此实现使用纯Java的方法调用源对象和目标对象之间进行映射,并非Java反射机制。

    与手工编写映射代码相比,MapStruct通过生成冗长且容易出错的代码来节省时间。在配置方法的约定之后,MapStruct使用了合理的默认值,但在配置或实现特殊行为时将不再适用。

    与动态映射框架相比,MapStruct具有以下优点:

    1,使用纯Java方法代替Java反射机制快速执行 2,编译时类型安全:只能映射彼此的对象和属性,不能映射一个Order实体到一个Customer DTO中等等 3,如果无法映射实体或属性,则在编译时清除错误报告

    maven依赖导入

    <dependency> <groupId>org.mapstruct</groupId> <artifactId>mapstruct</artifactId> <version>1.3.1.Final</version> <scope>compile</scope> </dependency> <dependency> <groupId>org.mapstruct</groupId> <artifactId>mapstruct-processor</artifactId> <version>1.3.1.Final</version> <scope>compile</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.5.1</version> <configuration> <source>1.8</source> <target>1.8</target> </configuration> </plugin> </plugins> </build>

    直接上代码

    首先定义一个泛型的基础映射器BaseMapping

    /** * <p> * MapStruct映射基类,直接继承接口即可使用通用方法,针对性指定需在继承类的接口方法上指定 * * 例: public interface xxxMapping extends BaseMapping<xxxDO, xxxDTO> * * 具体用法可参考单元测试,建议配合MapStruct Support插件使用 */ @MapperConfig(componentModel = "spring") public interface BaseMapping<S, T> { /** 正向映射 */ @InheritConfiguration T convertTo(S obj); /** 反向映射 */ @InheritInverseConfiguration(name = "convertTo") S convertFrom(T obj); /** 正向映射(List) */ default List<T> convertTo(List<S> list) { if (list == null) { return null; } List<T> result = new ArrayList<T>(list.size()); for (S s : list) { if (s == null) { continue; } result.add(convertTo(s)); } return result; } /** 反向映射(List) */ default List<S> convertFrom(List<T> list) { if (list == null) { return null; } List<S> result = new ArrayList<S>(list.size()); for (T t : list) { if (t == null) { continue; } result.add(convertFrom(t)); } return result; } /** 正向映射的后置处理,List映射会自动继承此配置 */ @AfterMapping default void handleAfterConvertTo(S src, @MappingTarget T dest) { afterConvertTo(src, dest); } /** 反向映射的后置处理,List映射会自动继承此配置 */ @AfterMapping default void handleAfterConvertFrom(T src, @MappingTarget S dest) { afterConvertFrom(src, dest); } /** 正向映射的后置处理,List映射会自动继承此配置 */ default void afterConvertTo(S src, T dest) { //TODO 覆盖此方法处理其他复杂转换逻辑 } /** 反向映射的后置处理,List映射会自动继承此配置 */ default void afterConvertFrom(T src, S dest) { //TODO 覆盖此方法处理其他复杂转换逻辑 } // /** 正向映射(stream) */ // @InheritConfiguration(name = "convertTo") // Stream<T> convertTo(Stream<S> stream); // // /** 反向映射(stream) */ // @InheritConfiguration(name = "convertFrom") // Stream<S> convertFrom(Stream<T> stream); }

    @MapperConfig:定义一个映射器配置 @InheritConfiguration:正向映射自动继承转换类型相同属性名相同的字段 @InheritInverseConfiguration:反向映射自动继承转换类型相同属性名相同的字段 @AfterMapping:后置处理器,在自动转换后可以实现不同类型不同字段之间的自定义转换

    实现具体的类型转换器

    这里实现一个具体的DO—>DTO的类型转换器EditionMapping

    /** * @author shihaowei * @date 2020/6/30 4:34 下午 */ @Mapper(componentModel = "spring") public interface EditionMapping extends BaseMapping<EditionDO, EditionDTO> { EditionMapping INSTANT = Mappers.getMapper(EditionMapping.class); @Override default void afterConvertTo(EditionDO src, EditionDTO dest) { dest.setOrderArray(src.getOrderarray()); dest.setOrderList(src.getOrderlist()); } }

    用于测试的两个实体类: EditionDO

    public class EditionDO { private Integer id; private String name; private Double memory; private String orderarray; private String orderlist; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Double getMemory() { return memory; } public void setMemory(Double memory) { this.memory = memory; } public String getOrderarray() { return orderarray; } public void setOrderarray(String orderarray) { this.orderarray = orderarray; } public String getOrderlist() { return orderlist; } public void setOrderlist(String orderlist) { this.orderlist = orderlist; } @Override public String toString() { return "EditionDO{" + "id=" + id + ", name='" + name + '\'' + ", memory=" + memory + ", orderarray='" + orderarray + '\'' + ", orderlist='" + orderlist + '\'' + '}'; } }

    EditionDTO

    /** * @author shihaowei * @date 2020-06-10 16:35 */ public class EditionDTO { private Integer id; private String name; private String memory; private String orderArray; private String orderList; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getMemory() { return memory; } public void setMemory(String memory) { this.memory = memory; } public String getOrderArray() { return orderArray; } public void setOrderArray(String orderArray) { this.orderArray = orderArray; } public String getOrderList() { return orderList; } public void setOrderList(String orderList) { this.orderList = orderList; } @Override public String toString() { return "EditionDTO{" + "id=" + id + ", name='" + name + '\'' + ", memory=" + memory + ", orderArray='" + orderArray + '\'' + ", orderList='" + orderList + '\'' + '}'; } }

    这里这两个类有两个字段名不同,需要在后置处理器里面去手动转换,只有类型相同属性名相同的属性才会自动转换,测试如下: 这里不同属性名转换成功。

    如果相同属性名,但是类型不同的话,转换会出现什么问题呢?然后我把EditionDTO里面memory属性的类型改成Date,而源EditionDO里面memory属性的类型是Double,然后我们进行测试:

    @Test public void getEditionById2(){ EditionDO editionDO = testService.getEditionById(1); System.err.println(editionDO); EditionDTO editionDTO = editionMapping.convertTo(editionDO); System.err.println(editionDTO); }

    这里的解决方法就是自定义转换类型并切去接口继承 DoubleToDateMapping

    /** * @author shihaowei * @date 2020/7/1 1:55 下午 */ public interface DoubleToDateMapping { default Date DoubleToDate(Double memory){ return new Date(); } }

    然后在mapping转化器里面去继承这个接口,然后可以自动把Double类型转换成Date

    @Mapper(componentModel = "spring") public interface EditionMapping extends BaseMapping<EditionDO, EditionDTO>, DoubleToDateMapping { EditionMapping INSTANT = Mappers.getMapper(EditionMapping.class); @Override default void afterConvertTo(EditionDO src, EditionDTO dest) { dest.setOrderArray(src.getOrderarray()); dest.setOrderList(src.getOrderlist()); } }

    然后我们再次运行测试: 这里显示转换成功了,这里使用Double转Date的例子不太合情理,但是达到的目的就是不同类型的转换

    注:针对相同属性字段不同类型的属性转换为String,正对常用的包装类型mapstruct封装李对这些常用类型的转换,不需要自己写转换

    Processed: 0.016, SQL: 9