Springboot集成MapStruct

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方法来赋值.
    手动调用get.set,对象之间属性多,类型不同的,会增加大量重复无意义的工作量,写法繁琐,没有技巧.适用于字段数量少的情况下适用

  • 方案二.使用apache的BeanUtil.copyProperties方法
    apache的BeanUtil. copyProperties,是采用对象反射来实现的,性能比较差,很多规范文件里是禁止适用,而且缺点也明显,必须转换的对象之间同属性名和类型,否则会忽略掉,这个可以自定义实现解决此问题,也不太灵活多变

  • 方案三.使用mapStruct
    基于注解实现,原理类似于lombok编译期动态生成赋值转换逻辑,方便操作,简化代码,对于包装类是自动拆箱封箱,线程安全的,性能也是可以比较强的.

如何使用

SpringBoot项目集成

maven添加依赖

    <dependency>
        <groupId>org.mapstruct</groupId>
        <artifactId>mapstruct</artifactId>
        <version>1.3.0.Final</version>
    </dependency>

添加插件指定编译期构建,解决项目中用lombok导致找不到类的问题

        <plugin>
            <groupId>org.apache.maven.plugins</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>org.mapstruct</groupId>
                        <artifactId>mapstruct-processor</artifactId>
                        <version>1.3.0.Final</version>
                    </path>
                    <path>
                        <groupId>org.projectlombok</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:StringDate 日期之间相互转换,通过 SimpleDateFormat,该值为 SimpleDateFormat 的日期格式

	ignore: 忽略这个字段
	expression : 自定义表达式引入
    	比如可以调用java某个方法 expression = "java(com.derun.util.DateUtils.strToDate(source.getCreateTime()))")
    	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 用于继承配置
// 其他的可官网上详细了解,只介绍常用的 https://mapstruct.org/

高级用法

两个类字段不一致时使用方法
/**
 * 运维签到信息参数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 = "record.samplingTime", target = "samplingTime", dateFormat = "yyyy-MM-dd HH:mm:ss"),
        @Mapping(source = "detail.openDoorTime", target = "openDoorTime", dateFormat = "yyyy-MM-dd HH:mm:ss"),
        @Mapping(source = "detail.closeDoorTime", target = "closeDoorTime", dateFormat = "yyyy-MM-dd HH:mm:ss"),
        @Mapping(source = "detail.bottleNumber",target = "bottleNumber", qualifiedByName="strToList",nullValueCheckStrategy = NullValueCheckStrategy.ALWAYS),
        @Mapping(source = "detail.imageUrl",target = "imageUrl",  qualifiedByName="strToList",nullValueCheckStrategy = NullValueCheckStrategy.ALWAYS),
        @Mapping(source = "record.id", 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;
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值