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

Posted 做猪呢,最重要的是开森啦

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了常用的实体类转换方式 - 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
    

以上是关于常用的实体类转换方式 - BeanUtil | MapStruct的主要内容,如果未能解决你的问题,请参考以下文章

mapstruct 实体转换及List转换,@Mapper注解转换

mapstruct 实体转换及List转换,@Mapper注解转换

BeanUtil 好用的工具类

Java常用工具类

BeanUtil体会

Java常用实体类--System类