1. 原始转换

提起对象转换,每个程序员都不陌生,比如项目中经常涉及到的DO、DTO、VO之间的转换,举个例子,假设现在有个OrderDTO,定义如下所示:

public class OrderDTO {
private long id; private Long userId; private String orderNo; private Date gmtCreated; // 省略get、set方法
}

有个OrderVO,定义如下所示:

public class OrderVO {
private long id; private long userId; private String orderNo; private Date gmtCreated; // 省略get、set方法
}

如果不使用任何转换工具,代码是下面这样的:

public static void main(String[] args) {
OrderDTO orderDTO = new OrderDTO();
orderDTO.setId(1L);
orderDTO.setUserId(123L);
orderDTO.setOrderNo("20210518000001");
orderDTO.setGmtCreated(new Date()); OrderVO orderVO = new OrderVO();
orderVO.setId(orderDTO.getId());
orderVO.setUserId(orderDTO.getUserId());
orderVO.setOrderNo(orderDTO.getOrderNo());
orderVO.setGmtCreated(orderDTO.getGmtCreated()); System.out.println(orderVO.getId());
System.out.println(orderVO.getUserId());
System.out.println(orderVO.getOrderNo());
System.out.println(orderVO.getGmtCreated());
}

运行结果:

2. 使用BeanUtils.copyProperties转换

因为项目中类似上面的转换多而繁琐,所以很多公司的项目中会使用Spring框架里的BeanUtils.copyProperties来做对象转换,代码如下所示:

OrderVO orderVO = new OrderVO();
BeanUtils.copyProperties(orderDTO, orderVO);

一行代码搞定,很方便,运行结果也和原来一模一样。

不过这个工具带来便利的同时,也带来了很多问题,稍微不注意就会踩坑,接下来就总结下使用这个工具常见的几个坑。

3. 踩坑经历

3.1 包装类型转基本类型问题

java.lang.IllegalArgumentException

细心的你可能会发现,OrderDTO中的userId字段,我定义的是Long类型:

而OrderVO中的userId字段,我定义的是long类型:

然后我们运行下下面所示的代码:

public static void main(String[] args) {
OrderDTO orderDTO = new OrderDTO();
orderDTO.setId(1L);
orderDTO.setUserId(null);
orderDTO.setOrderNo("20210518000001");
orderDTO.setGmtCreated(new Date()); OrderVO orderVO = new OrderVO();
BeanUtils.copyProperties(orderDTO, orderVO);
}

会看到代码抛了java.lang.IllegalArgumentException异常:

3.2 空格问题

假设OrderVO的orderNo字段,是用户自定义的,用户不小心输入了空格,使用BeanUtils.copyProperties后,空格会带入到OrderDTO的orderNo字段,如果不小心,就会把脏数据落到数据库(而我们希望的是去除空格再落库的),造成一系列后续问题:

public static void main(String[] args) {
OrderVO orderVO = new OrderVO();
orderVO.setId(1L);
orderVO.setUserId(123L);
// 模拟空格场景
orderVO.setOrderNo(" 20210518000001 ");
orderVO.setGmtCreated(new Date()); OrderDTO orderDTO = new OrderDTO();
BeanUtils.copyProperties(orderVO, orderDTO); System.out.println(orderDTO.getOrderNo());
}

运行结果:

3.3 查找不到字段引用

使用BeanUtils.copyProperties后,会看到字段并没有引用,其实是有用到的,如下图所示:

有些小伙伴在看代码时,看到字段没有地方引用,可能就忍不住想删掉,结果就导致真正使用该字段的地方取不到值,产生bug。

3.4 前端误传字段,直接把数据库覆盖了

如果接口定义的比较严谨,理论上是不应该存在这种情况的,不过凡事总有特殊,这里举个接口不严谨导致数据被覆盖的例子。

假如OrderVO和OrderDTO有如下2个字段:

/**
* 已收金额
* 单位:分
*/
private Long receivedAmount; /**
* 备注
*/
private String remark;

正常情况下,后端只应该使用前端传递的remark字段,receivedAmount字段不应该使用,但假如用户修改订单备注时,前端不小心传递了receivedAmount字段,并且赋值为null,这时使用BeanUtils.copyProperties后,OrderDTO里的receivedAmount字段就也为null,如果后端不知道前端传递了这个字段并且操作DB不够严谨,就会导致订单的已收金额被清空,很恐怖,而且不好排查原因。

4. 插件推荐

虽然BeanUtils.copyProperties工具提供了便利,但带来的问题也很多,因此很多公司(包含我现在所在的公司)都禁止在项目中使用该工具。

但重复的写对象转换,实在是太繁琐,效率太低了,这里推荐一个IDEA的插件GenerateAllSetter,可以一键生成对象的set方法,非常方便,如下图所示:

插件使用:

在需要生成set方法的对象上,按快捷键Option+Enter(Windows是Alt+Enter),会看到下图所示的选项:

点击后会自动生成所有字段(没有默认值)的赋值语句:

如果生成赋值语句时想带默认值,可以使用另一个选项:

效果如下所示:

使用BeanUtils.copyProperties踩坑经历的更多相关文章

  1. 『审慎』.Net4.6 Task 异步函数 比 同步函数 慢5倍 踩坑经历

    异步Task简单介绍 本标题有点 哗众取宠,各位都别介意(不排除个人技术能力问题) —— 接下来:我将会用一个小Demo 把 本文思想阐述清楚. .Net 4.0 就有了 Task 函数 —— 异步编 ...

  2. TiDB 深度实践之旅--真实“踩坑”经历

    美团点评 TiDB 深度实践之旅(9000 字长文 / 真实“踩坑”经历) 4   PingCAP · 154 天前 · 3956 次点击 这是一个创建于 154 天前的主题,其中的信息可能已经有所发 ...

  3. Net4.6 Task 异步函数 比 同步函数 慢5倍 踩坑经历

    Net4.6 Task 异步函数 比 同步函数 慢5倍 踩坑经历 https://www.cnblogs.com/shuxiaolong/p/DotNet_Task_BUG.html 异步Task简单 ...

  4. myeclipse使用db-brower连接到sqlserver2012踩坑经历

    myeclipse使用db-brower连接到sqlserver踩坑经历 首先得建立个角色 右键->创建登录名 权限开大点 连接设置 Driver template选择我选这个,格式按照我的写 ...

  5. sqlserver安装和踩坑经历

    sqlserver安装和踩坑经历 下载 下载 安装 大致是按照这个来的 安装教程 出错 windows系统安装软件弹出"Windows installer service could not ...

  6. Dubbo 服务 IP 注册错误踩坑经历

    个人博客地址 studyidea.cn,点击查看更多原创文章 踩坑 公司最近新建一个机房,需要将现有系统同步部署到新机房,部署完成之后,两地机房同时对提供服务.系统架构如下图: 这个系统当前对外采用 ...

  7. nginx搭建网站踩坑经历

    为了更好的阅读体验,请访问我的个人博客 前言 早上刷抖音刷到一个只需要三步的nginx搭建教程(视频地址),觉得有些离谱,跟着复现了一遍,果然很多地方不严谨并且省略了大量步骤,对于很多不了解linux ...

  8. 【踩坑经历】一次Asp.NET小网站部署踩坑和解决经历

    2013年给1个大学的小客户部署过一个小型的Asp.NET网站,非常小,用的sqlite数据库,今年人家说要换台服务器,要重新部署一下,好吧,虽然早就过了服务时间,但无奈谁叫人家是客户了,二话不说,上 ...

  9. RocketMQ同一个消费者唯一Topic多个tag踩坑经历

    最近做的项目的一个版本需求中,需要用到MQ,对数据记录进行异步落库,这样可以减轻数据库的压力,同时可以抗住大量的数据落库.这里需要说明一下本人用到的MQ是公司自己在阿里的RokectMQ的基础上进行封 ...

随机推荐

  1. 使用命令行编译Qt程序

    code[class*="language-"], pre[class*="language-"] { color: rgba(51, 51, 51, 1); ...

  2. OO第四单元——基于UML的UML解析器总结&OO课程总结

    OO第四单元--基于UML的UML解析器总结&OO课程总结 前言:一学期愉快(痛苦)的OO课程学习结束了,OO几个单元作业都各有特色,实验也各有特色,仔细回味起来,不再是单纯的敲代码(但自己还 ...

  3. spring-cloud-sleuth分布式服务跟踪

    通过之前的 Spring Cloud 组件学习, 实际上我们已经能够通过使用它们搭建起一 个基础的微服务架构系统来实现业务需求了. 但是, 随着业务的发展, 系统规模也会变得越来越大, 各微服务间的调 ...

  4. 记一次踩坑之路之Ubuntu未导入镜像前配置docke/docker-composer

    更新 apt 包索引与升级 sudo apt-get update sudo apt-get upgrade 安装 apt 依赖包,用于通过HTTPS来获取仓库: sudo apt-get insta ...

  5. OrchardCore 如何动态加载模块?

    前言 今天,我们再次讨论下OrchardCore,通过初期调研,我们项目采用OrchardCore底层设施支持模块化,同时根据业务场景,额外还需支持二次开发,于是有了本文,若有不同解决方案,欢迎留言探 ...

  6. Java基础 - List的两个子类的特点

    List两个子类的特点 List的两个子类的特点 因为两个类都实现了List接口,所以里面的方法都差不多,那这两个类都有什么特点呢? ArrayList: 底层数据结构是数组,查询快,增删慢. Lin ...

  7. Java(171-194)【接口、多态】

    1.接口概述与生活举例 接口就是一种公共的规范标准 只要符合规范标准,就可以大家通用 2.接口的定义基本格式  public interface 接口名称 {       // 抽象方法      / ...

  8. 附034.Kubernetes_v1.21.0高可用部署架构二

    kubeadm介绍 kubeadm概述 Kubeadm 是一个工具,它提供了 kubeadm init 以及 kubeadm join 这两个命令作为快速创建 kubernetes 集群的最佳实践. ...

  9. matlab文件管理

    当前文价夹浏览器以及路径管理器   在主页面左侧有单独的窗口进行显示,可以显示当前目录下的文件并提供文件搜索功能. 搜索路径 搜索先后步骤 输入字符串"polyfit" (1)检查 ...

  10. Day05_17_面向对象编程

    Java面向对象 面向对象编程 和 面向过程编程 举个例子: 用面向过程的方法写出来的程序是一份蛋炒饭,而用面向对象写出来的程序是一份盖浇饭. 面向过程编程: 主要关注事物因果关系的实现,面向过程就是 ...