常用的实体类转换方式 - BeanUtil | MapStruct

个人常用的是hutool的BeanUtil.copyProperties以及MapStruct,这里也主要介绍一下常用用法

0. 相关依赖:
		<!-- hutool工具类的依赖 -->
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.7.12</version>
        </dependency>

        <!-- jdk8以下就使用mapstruct -->
        <dependency>
            <groupId>org.mapstruct</groupId>
            <artifactId>mapstruct-jdk8</artifactId>
            <version>1.3.0.Final</version>
        </dependency>
        <dependency>
            <groupId>org.mapstruct</groupId>
            <artifactId>mapstruct-processor</artifactId>
            <version>1.3.0.Final</version>
        </dependency>

		<!-- lombok依赖 -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.22</version>
        </dependency>
1. 实体类信息:
  • 主要区别:PO和VO的name不一样,然后PO的字段多一些

在这里插入图片描述
在这里插入图片描述

2. BeanUtil方法转换:
2.1. 实体类转实体类(copyProperties):

 po为原实体类,vo为转换后的实体类

  • 默认字段匹配上的字段都会转换,所以vo没有userName/name字段,因为po的userName和vo的name对不上
  • vo1转换的时候设置了忽略age字段转换,所以vo1不会转换赋值age字段
  • vo2通过CopyOptions进行了字段映射,将userName的值映射到name上,所以vo2的name是有值的
    @Test
    public void po2vo() {
        UserInfoPO po = UserInfoPO.builder().userId(1L).userName("zhangsan").age("18").build();
        System.out.println(JSON.toJSONString(po)); // {"age":"18","userId":1,"userName":"zhangsan"}

        UserInfoVO vo = BeanUtil.copyProperties(po, UserInfoVO.class);
        System.out.println(JSON.toJSONString(vo)); // {"age":"18","userId":1}

        UserInfoVO vo1 = BeanUtil.copyProperties(po, UserInfoVO.class, "age");
        System.out.println(JSON.toJSONString(vo1)); // {"userId":1}

        UserInfoVO vo2 = UserInfoVO.builder().build();
        HashMap<String, String> map = Maps.newHashMap();
        map.put("userName", "name");
        CopyOptions copyOptions = CopyOptions.create().setFieldMapping(map);
        BeanUtil.copyProperties(po, vo2, copyOptions);
        System.out.println(JSON.toJSONString(vo2)); // {"age":"18","name":"zhangsan","userId":1}
    }
2.2. 实体类集合转实体类集合(copyToList):

 list为原实体类集合,vos为转换后的实体类集合

  • 默认字段匹配上的字段都会转换,所以vos没有userName/name字段,因为po的userName和vo的name对不上
  • vos1转换的时候设置了忽略age字段转换,所以vos1不会转换赋值age字段
  • vos2通过CopyOptions进行了字段映射,将userName的值映射到name上,所以vos2的name是有值的
    @Test
    public void pos2vos() {
        UserInfoPO po = UserInfoPO.builder().userId(1L).userName("zhangsan").age("18").build();
        ArrayList<UserInfoPO> list = Lists.newArrayList(po);
        System.out.println(JSON.toJSONString(list)); // [{"age":"18","userId":1,"userName":"zhangsan"}]

        List<UserInfoVO> vos = BeanUtil.copyToList(list, UserInfoVO.class);
        System.out.println(JSON.toJSONString(vos)); // [{"age":"18","userId":1}]

        CopyOptions copyOptions = CopyOptions.create().setIgnoreProperties("age");
        List<UserInfoVO> vos1 = BeanUtil.copyToList(list, UserInfoVO.class, copyOptions);
        System.out.println(JSON.toJSONString(vos1)); // [{"userId":1}]

        HashMap<String, String> map = Maps.newHashMap();
        map.put("userName", "name");
        CopyOptions copyOption1 = CopyOptions.create().setFieldMapping(map);
        List<UserInfoVO> vos2 = BeanUtil.copyToList(list, UserInfoVO.class, copyOption1);
        System.out.println(JSON.toJSONString(vos2)); // [{"age":"18","name":"zhangsan","userId":1}]
    }
2.3. 实体类集合转Map(beanToMap):

直接调方法转即可,还可以设置是否进行key的驼峰转换

    @Test
    public void po2map() {
        UserInfoPO po = UserInfoPO.builder().userId(1L).userName("zhangsan").age("18").build();
        System.out.println(JSON.toJSONString(po)); // {"age":"18","userId":1,"userName":"zhangsan"}

        Map<String, Object> map = BeanUtil.beanToMap(po);
        System.out.println(JSON.toJSONString(map)); // {"age":"18","userId":1,"userName":"zhangsan"}

        Map<String, Object> map1 = BeanUtil.beanToMap(po, true, false);
        System.out.println(JSON.toJSONString(map1)); // {"user_id":1,"user_name":"zhangsan","age":"18"}
    }
2.4. 嵌套实体类转换:

  UserInfoPO嵌套了JobPO,po为原实体类,vo为转换后的实体类

  • 默认字段匹配上的字段都会转换,所以vo的name没有值,嵌套的job的jname也没有值,因为匹配不上
  • 要像嵌套的实体类也能映射上,需要像如下vo2这样copyProperties转换两次
    @Test
    public void po2voNested() {
        JobPO jobPO = JobPO.builder().jobName("java").jobId(1L).build();
        UserInfoPO po = UserInfoPO.builder().userId(1L).userName("lisi").job(jobPO).build();
        System.out.println(JSON.toJSONString(po)); // {"job":{"jobId":1,"jobName":"java"},"userId":1,"userName":"lisi"}

        UserInfoVO vo = BeanUtil.copyProperties(po, UserInfoVO.class);
        System.out.println(JSON.toJSONString(vo)); // {"job":{"jobId":1},"userId":1}

        UserInfoVO vo1 = BeanUtil.copyProperties(po, UserInfoVO.class, "age");
        System.out.println(JSON.toJSONString(vo1)); // {"job":{"jobId":1},"userId":1}

        UserInfoVO vo2 = UserInfoVO.builder().build();
        HashMap<String, String> map = Maps.newHashMap();
        map.put("userName", "name");
        map.put("jobName", "jName");
        CopyOptions copyOptions = CopyOptions.create().setFieldMapping(map);
        BeanUtil.copyProperties(po, vo2, copyOptions);
        System.out.println(JSON.toJSONString(vo2)); // {"job":{"jobId":1},"name":"lisi","userId":1}

        BeanUtil.copyProperties(po.getJob(), vo2.getJob(), copyOptions);
        System.out.println(JSON.toJSONString(vo2)); // {"job":{"jName":"java","jobId":1},"name":"lisi","userId":1}
    }
  • 如果像一些简单的bean或者bean集合,不一致的字段比较少,用BeanUtil还是可以接受的
  • 但像字段比较多或者嵌套实体类或者不一致的字段比较多,首先需要自定义CopyOptions,代码比较不雅观
  • BeanUtil转换过程是基于反射的,性能也会有点影响
3. MapStruct方法转换:

MapStruct网上也有很多介绍了,它不是基于反射的,在编译时就帮我们set值,所以相对高效一点

3.1. MapStruct的cover接口:

 【说明】:

  • @Mapper是mapstruct的注解
  • 通过Mappers.getMapper定义一个转换对象
  • 多个字段名称不一致,可以使用@Mappings进行映射关联,仅有一个可以直接使用@Mapping
  • 要想忽略某些字段转换,定义@Mapping的属性ignore = true即可
  • 同一个接口文件,存在多个相同入参对应相同返回值类型,如果不做处理,会报下方说明第二个异常
  • 处理时可以使用@IterableMapping来指定编译用哪个方法进行转换
  • @AfterMapping可以对转换后的实体进行一些处理,比如将name转为大写;同样也存在@BeforeMapping

·
 【异常说明】:

  • 编译时报java Internal error in the mapping processor java.lang.NullPointerException,和idea有关,可以参考设置
  • 允许时报MapStruct: Ambiguous mapping methods found for mapping collection element,可以参考修改
  • 使用@AfterMapping时,可能存在与@Builder冲突,导致方法不执行情况,可以参考链接
@Mapper
public interface UserInfoCover {

    UserInfoCover INSTANCE = Mappers.getMapper(UserInfoCover.class);

    @Mappings({
            @Mapping(source = "userName", target = "name")
    })
    UserInfoVO toConvertUserInfoVo(UserInfoPO po);

    @Mapping(source = "jobName", target = "jName")
    JobVO toConvertJobVo(JobPO po);

    @IterableMapping(qualifiedByName = "toConvertUserInfoVoWithNoAge")
    List<UserInfoVO> toConvertUserInfoVos(List<UserInfoPO> pos);

    @Named("toConvertUserInfoVoWithNoAge")
    @Mappings({
            @Mapping(source = "userName", target = "name"),
            @Mapping(source = "age", target = "age", ignore = true)
    })
    UserInfoVO toConvertUserInfoVoWithNoAge(UserInfoPO po);

    @Mapping(source = "userName", target = "name")
    UserInfoVO toConvertAfter(UserInfoPO po);

    @AfterMapping
    default void toConvertAfter(UserInfoPO po, @MappingTarget UserInfoVO.UserInfoVOBuilder vo) {
        vo.name(StringUtils.toRootUpperCase(po.getUserName()));
    }
}
3.1. MapStruct转换使用:

 【说明】:

  • UserInfoVO拥有嵌套实体类,所以编写上述接口文件时,不仅要编写UserInfoVO的转换方法,还需要编写JobVO的转换方法
  • 同样,集合转换时不仅要编写toConvertUserInfoVos转集合的方法,还需要对应的实体类转换方法
  • 总体像实体类转换和集合转换,MapStruct还是十分间接优雅的
    @Test
    public void po2voNested() {
        JobPO jobPO = JobPO.builder().jobName("java").jobId(1L).build();
        UserInfoPO po = UserInfoPO.builder().userId(1L).userName("lisi").job(jobPO).build();
        System.out.println(JSON.toJSONString(po));  // {"job":{"jobId":1,"jobName":"java"},"userId":1,"userName":"lisi"}

        UserInfoVO vo = UserInfoCover.INSTANCE.toConvertUserInfoVo(po);
        System.out.println(JSON.toJSONString(vo));  // {"job":{"jName":"java","jobId":1},"name":"lisi","userId":1}
    }

    @Test
    public void pos2vos() {
        UserInfoPO po = UserInfoPO.builder().userId(1L).userName("zhangsan").age("18").build();
        ArrayList<UserInfoPO> list = Lists.newArrayList(po);
        System.out.println(JSON.toJSONString(list)); // [{"age":"18","userId":1,"userName":"zhangsan"}]

        List<UserInfoVO> vos = UserInfoCover.INSTANCE.toConvertUserInfoVos(list);
        System.out.println(JSON.toJSONString(vos)); // [{"name":"zhangsan","userId":1}]
    }
    
    @Test
    public void po2voAfter() {
        JobPO jobPO = JobPO.builder().jobName("java").jobId(1L).build();
        UserInfoPO po = UserInfoPO.builder().userId(1L).userName("lisi").job(jobPO).build();
        System.out.println(JSON.toJSONString(po));  // {"job":{"jobId":1,"jobName":"java"},"userId":1,"userName":"lisi"}

        UserInfoVO userInfoVO = UserInfoCover.INSTANCE.toConvertAfter(po);
        System.out.println(JSON.toJSONString(userInfoVO));  // {"job":{"jName":"java","jobId":1},"name":"LISI","userId":1}
    }
  • 1
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值