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. python使用try...except语句处理异常

    try....except语句语法格式: try: <语句> except(异常名称): <语句> 注意在except语句中的括号中的异常名称是可以省略的,当省略时就是全捕捉 ...

  2. toastr通知插件的使用

    /显示一个警告,没有标题 toastr.warning('My name is Inigo Montoya. You killed my father, prepare to die!') 显示一个成 ...

  3. PhpStorm/WebStorm实用技巧

    我常用的IDE设置和功能 1) 使用IDE管理远程主机 Tools -> Deployment -> Browse Remote Host 其中功能十分强大 自己去探索 关键提示: 手动/ ...

  4. WPF -- 使用当前进程打开自定义文件的一种方式

    问题描述 当双击打开自定义格式的文件时,希望使用当前正在运行的进程,而不是另起一个进程. 本文介绍一种方式解决如上问题,方案参考user3582780的解答 设置自定义文件格式的默认打开方式 参考链接 ...

  5. Java String系列

    String详解, String和CharSequence区别, StringBuilder和StringBuffer的区别 (String系列之1) StringBuilder 详解 (String ...

  6. 【CTF】XCTF 我们的秘密是绿色的 writeup

    题目来源:SSCTF-2017 题目链接:https://adworld.xctf.org.cn/task/answer?type=misc&number=1&grade=1& ...

  7. 自定义grub主题

    1 概述 自定义grub引导菜单的主题,笔者的linux是deepin,感觉默认的grub主题不太好看,嗯,没办法,就是想改了. 这篇文章主要是修改/boot/grub/grub.cfg以及/boot ...

  8. 17. Vue2.4+新增属性$listeners

    现在我们来讨论一种情况,A组件与C组件怎么通信,我们有多少种解决方案? 我们使用VueX来进行数据管理,但是如果项目中多个组件共享状态比较少,项目比较小,并且全局状态比较少,那使用VueX来实现该功能 ...

  9. OAuth 2.0 了解了,OAuth 2.1 呢?

    OAuth 2.0 OAuth 2.0 是工业级标准授权协议. OAuth 2.0 聚焦于客户端开发者便利性,为网页应用程序.桌面客户端.手机.客厅设备提供特定的授权流程. RFC6749 OAuth ...

  10. 基于MATLAB的手写公式识别(2)

    基于MATLAB的手写公式识别 图像的预处理(除去噪声.得到后续定位分割所需的信息.) 预处理其本质就是去除不需要的噪声信息,得到后续定位分割所需要的图像信息.图像信息在采集的过程中由于天气环境的影响 ...