MapStruct
插件是干什么的?
首先普及下VO,DO,PO,DTO的概念
**VO(View Object):**视图对象,用于展示层,它的作用是把某个指定页面(或组件)的所有数据封装起来。
**DTO(Data Transfer Object):**数据传输对象,泛指用于展示层与服务层之间的数据传输对象。
**DO(Domain Object):**领域对象,就是从现实世界中抽象出来的有形或无形的业务实体。
**PO(Persistent Object):**持久化对象,它跟持久层(通常是关系型数据库)的数据结构形成一一对应的映射关系,如果持久层是关系型数据库,那么,数据表中的每个字段(或若干个)就对应PO的一个(或若干个)属性。
示例解释 引用于网络中的解释
-
用户发出请求(可能是填写表单),表单的数据在展示层被匹配为VO。
-
展示层把VO转换为服务层对应方法所要求的DTO,传送给服务层。
-
服务层首先根据DTO的数据构造(或重建)一个DO,调用DO的业务方法完成具体业务。一般DO的我们会省略掉,直接转PO操作数据去了
-
服务层把DO转换为持久层对应的PO(可以使用ORM工具,也可以不用),调用持久层的持久化方法,把PO传递给它,完成持久化操作。
-
对于一个逆向操作,如读取数据,也是用类似的方式转换和传递,略。
不同对象之间的转换问题
-
方案一.使用对象的get,set方法来赋值.
手动调用,对象之间属性多,类型不同的,会增加大量重复无意义的工作量,写法繁琐,没有技巧.适用于字段数量少的情况下适用 -
方案二.使用apache的方法
apache的BeanUtil. copyProperties,是采用对象反射来实现的,性能比较差,很多规范文件里是禁止适用,而且缺点也明显,必须转换的对象之间同属性名和类型,否则会忽略掉,这个可以自定义实现解决此问题,也不太灵活多变 -
方案三.使用mapStruct
基于注解实现,原理类似于lombok编译期动态生成赋值转换逻辑,方便操作,简化代码,对于包装类是自动拆箱封箱,线程安全的,性能也是可以比较强的.
如何使用
SpringBoot项目集成
maven添加依赖
<dependency>
<groupId></groupId>
<artifactId>mapstruct</artifactId>
<version>1.3.</version>
</dependency>
添加插件指定编译期构建,解决项目中用lombok导致找不到类的问题
<plugin>
<groupId></groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
<annotationProcessorPaths>
<path>
<groupId></groupId>
<artifactId>mapstruct-processor</artifactId>
<version>1.3.</version>
</path>
<path>
<groupId></groupId>
<artifactId>lombok</artifactId>
<version>1.18.10</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
创建接口
@Mapper(componentModel="spring",uses = {CommonUtils.class})
public interface OpsConverter {
/**
* 运维单DTO转 运维单DO
*
* @param dto 运维单DTO
* @return Ops
*/
Ops opsConverter(OpsSmokeDTO dto);
* 运维单DO转 运维单VO
*
* @param ops 运维单DO
* @return Ops
*/
@Mappings({
@Mapping(source = "createTime",target = "createTime",qualifiedByName="dateToStr")
})
OpsSmokeVO opsVOConverter(Ops ops);
/**
* 运维其他参数信息DTO 转 DO
* @param dto 运维其他参数信息DTO
* @return OpsOtherPram
*/
@Mappings({
@Mapping(source = "onTime",target = "onTime",qualifiedByName="strToDate",nullValueCheckStrategy = NullValueCheckStrategy.ALWAYS),
@Mapping(source = "offTime",target = "offTime",qualifiedByName="strToDate",nullValueCheckStrategy = NullValueCheckStrategy.ALWAYS),
})
OpsOtherPram opsOtherPramConverter(OpsOtherPramDTO dto);
}
使用方式
// 采用了Spring管理模式可以直接@Autowired
@Autowired
OpsConverter opsConverter;
//方法中使用 dto为传入的前端传入的入参
Ops ops = opsConverter.opsConverter(dto);
相关注解含义
@Mapper 只有在接口加上这个注解, MapStruct 才会去实现该接口,此@Mapper非mybatis的@Mapper
@Mapper 里有个 componentModel 属性,主要是指定实现类的类型,一般用到两个
default:默认,可以通过 Mappers.getMapper(Class) 方式获取实例对象
spring:在接口的实现类上自动添加注解 @Component,可通过 @Autowired 方式注入
uses:表示引用的哪些类 @Mapper(componentModel="spring",uses = {DateUtils.class}),表示引用哪个类方法
@Mapping:属性映射,若源对象属性与目标对象名字一致,会自动映射对应属性 推荐此方式
source:源属性 直接写源属性名 源属性中有多个嵌套对象可以使用`.`来引用,比如User对象中有个Org的名称引用,可以写user.org.name
target:目标属性 直接写目标属性名 同源属性用法
dateFormat:String 到 Date 日期之间相互转换,通过 SimpleDateFormat,该值为 SimpleDateFormat 的日期格式
ignore: 忽略这个字段
expression : 自定义表达式引入
比如可以调用java某个方法 expression = "java((()))")
public class DateUtils {
public static LocalDateTime strToDate(String str){
DateTimeFormatter df = DateTimeFormatter.ofPattern("yyy-MM-dd HH:mm:ss");
return LocalDateTime.parse(str,df);
}
}
qualifiedByName : 根据名称找方法,需要配合@Mapper(uses={DateUtils.class}),直接填入方法名即可,注意需要方法名上写注解@Named
/**
* 日期字符串转localDateTime
*
* @param str 日期字符串
* @return LocalDateTime
*/
@Named("strToDate")
public static LocalDateTime strToDate(String str) {
if (StrUtil.isBlank(str)) {
return null;
}
return LocalDateTimeUtil.parse(str, "yyyy-MM-dd HH:mm:ss");
}
@Mappings:配置多个@Mapping@MappingTarget 用于更新已有对象@InheritConfiguration 用于继承配置
// 其他的可官网上详细了解,只介绍常用的 /
高级用法
两个类字段不一致时使用方法
/**
* 运维签到信息参数DTO 转DO
* @param signIn 签到信息
* @return OpsSignIn
*/
@Mappings({
@Mapping(source = "createTime",target = "createDate",qualifiedByName="dateToStr")
})
OpsSignIn opsSignInConverter(OpsSignInDTO signIn);
多对象转换
/**
* 取样记录,取样记录详情 转换为 取样详情VO
*
* @param record 取样记录实体
* @param detail 取样记录详情实体
* @return
*/
@Mappings({
@Mapping(source = "", target = "samplingTime", dateFormat = "yyyy-MM-dd HH:mm:ss"),
@Mapping(source = "", target = "openDoorTime", dateFormat = "yyyy-MM-dd HH:mm:ss"),
@Mapping(source = "", target = "closeDoorTime", dateFormat = "yyyy-MM-dd HH:mm:ss"),
@Mapping(source = "",target = "bottleNumber", qualifiedByName="strToList",nullValueCheckStrategy = NullValueCheckStrategy.ALWAYS),
@Mapping(source = "",target = "imageUrl", qualifiedByName="strToList",nullValueCheckStrategy = NullValueCheckStrategy.ALWAYS),
@Mapping(source = "", target = "id")
})
SamplingRecordDetailVO detailConverter(SamplingRecord record, SamplingRecordDetail detail);
集合对象转换
/**
* 运维系统参数 DO 转 DTO
* @param pramList 运维系统参数
* @return List<OpsSysPram>
*/
List<OpsSysPramDTO> opsSysPramDTOConverter(List<OpsSysPram> pramList);
编译后的代码查看
目录在target->generated-sources目录中,编译后的文件源代码
@Override
public SamplingRecordDetailVO detailConverter(SamplingRecord record, SamplingRecordDetail detail) {
if ( record == null && detail == null ) {
return null;
}
SamplingRecordDetailVO samplingRecordDetailVO = new SamplingRecordDetailVO();
if ( record != null ) {
if ( record.getSamplingTime() != null ) {
samplingRecordDetailVO.setSamplingTime( DateTimeFormatter.ofPattern( "yyyy-MM-dd HH:mm:ss" ).format( record.getSamplingTime() ) );
}
samplingRecordDetailVO.setId( record.getId() );
samplingRecordDetailVO.setOddNumber( record.getOddNumber() );
samplingRecordDetailVO.setOrgName( record.getOrgName() );
samplingRecordDetailVO.setOutletName( record.getOutletName() );
samplingRecordDetailVO.setDeviceName( record.getDeviceName() );
samplingRecordDetailVO.setOpsOrgName( record.getOpsOrgName() );
samplingRecordDetailVO.setOpsUserName( record.getOpsUserName() );
}
if ( detail != null ) {
if ( detail.getCloseDoorTime() != null ) {
samplingRecordDetailVO.setCloseDoorTime( DateTimeFormatter.ofPattern( "yyyy-MM-dd HH:mm:ss" ).format( detail.getCloseDoorTime() ) );
}
if ( detail.getOpenDoorTime() != null ) {
samplingRecordDetailVO.setOpenDoorTime( DateTimeFormatter.ofPattern( "yyyy-MM-dd HH:mm:ss" ).format( detail.getOpenDoorTime() ) );
}
List<String> list = CommonUtils.strToList( detail.getImageUrl() );
if ( list != null ) {
samplingRecordDetailVO.setImageUrl( list );
}
List<String> list1 = CommonUtils.strToList( detail.getBottleNumber() );
if ( list1 != null ) {
samplingRecordDetailVO.setBottleNumber( list1 );
}
samplingRecordDetailVO.setRemark( detail.getRemark() );
samplingRecordDetailVO.setImageLocation( detail.getImageLocation() );
}
return samplingRecordDetailVO;
}