1、什么是DTO、VO、BO、PO、DO、POJO
POJO的定义是无规则简单的对象,在日常的代码分层中pojo会被分为VO、BO、 PO、 DTO
VO (view object/value object)表示层对象
1、前端展示的数据,在接口数据返回给前端的时候需要转成VO
2、个人理解使用场景,接口层服务中,将DTO转成VO,返回给前台
B0(bussines object)业务层对象
1、主要在服务内部使用的业务对象
2、可以包含多个对象,可以用于对象的聚合操作
3、个人理解使用场景,在服务层服务中,由DTO转成BO然后进行业务处理后,转成DTO返回到接口层
PO(persistent object)持久对象
1、出现位置为数据库数据,用来存储数据库提取的数据
2、只存储数据,不包含数据操作
3、个人理解使用场景,在数据库层中,获取的数据库数据存储到PO中,然后转为DTO返回到服务层中
DTO(Data Transfer Object)数据传输对象
1、在服务间的调用中,传输的数据对象
2、个人理解,DTO是可以存在于各层服务中(接口、服务、数据库等等)服务间的交互使用DTO来解耦
DO(domain object)领域实体对象
DO 现在主要有两个版本:
①阿里巴巴的开发手册中的定义,DO( Data Object)这个等同于上面的PO
②DDD(Domain-Driven Design)领域驱动设计中,DO(Domain Object)这个等同于上面的BO
参考文档:
1 https://juejin.cn/post/6952848675924082718 2 https://juejin.cn/post/6844904046097072141 3 https://zhuanlan.zhihu.com/p/264675395
2、插件如何完成转化
插件名称:Simple Object Copy
- 定义方法出入参
- 光标定位方法内,使用快捷键ALT+INSERT(WIN) 、 command + N(mac) ,或者右键鼠标选择Generate,弹出生成选项框后,选择genCopyMethod,代码就生成好了


复杂对象转化展示

1@Data 2public class UserVO { 3 private String name; 4 private Date entryDate; 5 private String userId; 6 private List<RoleVO> roleList; 7 private RoomVO room; 8 9 public static UserVO convertToUserVO(UserDTO item) { 10 if (item == null) { 11 return null; 12 } 13 UserVO result = new UserVO(); 14 result.setName(item.getName()); 15 result.setEntryDate(item.getEntryDate()); 16 result.setUserId(item.getUserId()); 17 List<RoleDTO> roleList = item.getRoleList(); 18 if (roleList == null) { 19 result.setRoleList(null); 20 } else { 21 result.setRoleList(roleList.stream().map(UserVO::convertToRoleVO).collect(Collectors.toList()); 22 } 23 result.setRoom(convertToRoomVO(item.getRoom())); 24 return result; 25 } 26 27 public static RoomVO convertToRoomVO(RoomDTO item) { 28 if (item == null) { 29 return null; 30 } 31 RoomVO result = new RoomVO(); 32 result.setRoomId(item.getRoomId()); 33 result.setBuildingId(item.getBuildingId()); 34 result.setRoomName(); 35 result.setBuildingName(); 36 return result; 37 } 38 39 public static RoleVO convertToRoleVO(RoleDTO item) { 40 if (item == null) { 41 return null; 42 } 43 RoleVO result = new RoleVO(); 44 result.setRoleId(item.getRoleId()); 45 result.setRoleName(item.getRoleName()); 46 result.setCreateTime(item.getCreateTime()); 47 return result; 48 } 49} 50 51@Data 52public class UserDTO { 53 private String name; 54 private Date entryDate; 55 private String userId; 56 private List<RoleDTO> roleList; 57 private RoomDTO room; 58} 59 60@Data 61public class RoleVO { 62 private String roleId; 63 private String roleName; 64 private LocalDateTime createTime; 65} 66 67@Data 68public class RoleDTO { 69 private String roleId; 70 private String roleName; 71 private LocalDateTime createTime; 72} 73 74@Data 75public class RoomVO { 76 private String roomId; 77 private String buildingId; 78 private String roomName; 79 private String buildingName; 80 81} 82 83@Data 84public class RoomDTO { 85 private String roomId; 86 private String buildingId; 87}
3、其他转化方式
1.无入侵 市面上有很多类似的工具类,比较常用的有
- 1、Spring BeanUtils (copyProperties)
- 2、Cglib BeanCopier (copyProperties)
- 3、Apache BeanUtils (copyProperties)
- 4、Apache PropertyUtils (copyProperties)
- 5、Dozer
- 6、mapstruct
- 7、JSON 序列化 再反序列化
这些工具,不仅要引入相应的依赖jar包,而且对代码有入侵,要调用对应得api方法才能进行转化,一旦遇到类型不一致,字段名稍有变动,就需要另写java代码补全字段,整体代码非常丑陋。
举例:
1.mapstruct
同样的代码,,不仅要引入依赖、写如下转化mapper,还要,在对应地方调用对应api(代码入侵验证),然而Simple Object Copy 只需要一键生成。
RoomDTO中不存在的roomName、buildingName还要mapstruct另写方法,很容易忽略。源实体中不存在的属性,没有提示,小心前端总是问为啥都是null。
在Simple Object Copy 插件代码生成后,不存在的字段也生成了空方法,直接编译提示补充,不容易忽略
需要手写的代码:
1@Mapper(componentModel = "spring",uses = {RoleVOMapper.class,RoomVOMapper.class}) 2public interface UserMapper { 3 UserConverter INSTANCE = Mappers.getMapper(UserConverter.class); 4 5 UserVO toUserVO(UserDTO userDTO); 6} 7 8@Mapper(componentModel = "spring") 9public interface RoleMapper { 10 RoleVO toRoleVO(RoleDTO roleDTO); 11} 12 13@Mapper(componentModel = "spring") 14public interface RoomMapper { 15 RoomVO toRoomVO(RoomDTO roomDTO); 16} 17 18//调用示例 19public class Main { 20 public static void main(String[] args) { 21 UserDTO user = ; 22 UserVO userVO = UserMapper.INSTANCE.toUserVO(user); 23 userVO.getRoomVO().setRoomName("大厅1"); 24 userVO.getRoomVO().setBuildingName("尚德楼"); 25 } 26} 27
2.BeanUtils
性能稍差。
不支持复杂对象还是要写大量代码,代码字段不清晰不易理解,别人接手难。
RoomDTO中不存在的roomName、buildingName还要BeanUtils另写方法,很容易忽略。源实体中不存在的属性,没有提示,小心前端总是问为啥都是null。
需要手写的代码
1@Data 2public class UserVO { 3 private String name; 4 private Date entryDate; 5 private String userId; 6 private List<RoleVO> roleList; 7 private RoomVO room; 8 public static UserVO convertToUserVO(UserDTO item) { 9 if (item == null) { 10 return null; 11 } 12 UserVO result = new UserVO(); 13 BeanUtils.copyProperties(item,result); 14 List<RoleDTO> roleList = item.getRoleList(); 15 if (roleList == null) { 16 result.setRoleList(null); 17 } else { 18 result.setRoleList(roleList.stream().map(UserVO::convertToRoleVO).collect(Collectors.toList()); 19 } 20 result.setRoom(convertToRoomVO(item.getRoom())); 21 return result; 22 } 23 24 public static RoomVO convertToRoomVO(RoomDTO item) { 25 if (item == null) { 26 return null; 27 } 28 RoomVO result = new RoomVO(); 29 BeanUtils.copyProperties(item,result); 30 //这里没有代码提示,需要自己写,小心前端总是问为啥都是null 31 result.setRoomName(); 32 result.setBuildingName(); 33 return result; 34 } 35 36 public static RoleVO convertToRoleVO(RoleDTO item) { 37 if (item == null) { 38 return null; 39 } 40 RoleVO result = new RoleVO(); 41 BeanUtils.copyProperties(item,result); 42 return result; 43 } 44} 45
2.性能优势
相比上面的工具类,不是使用反射、就是是用代理、序列化操作。相比于纯正的set方法去转化,差距不是一个量级。此次不赘述。
3.灵活性、兼容性
跟上述工具类相比插件有很大优势,不再赘述,下面我们比较一下,我之前常用的idea插件generateO2O
| 我方插件 | 特点 | 对比插件 | 特点 |
|---|---|---|---|
| Simple Object Copy | 依据返回值为主,根据字段名去匹配 ,不会导致返回值漏属性 | generateO2O | 以入参为主匹配字段,存在漏属性的情况 |
| Simple Object Copy | 支持对象包含对象、对象包含list、set集合的转化 | generateO2O | 不支持子对象的转化、不支持list泛型不同的转化 |
| Simple Object Copy | 相同出入参类名,生成全限定类名 | generateO2O | 同类名出现问题 |
在此推荐其他一个我常用插件:generateAllSetter,搭配食用更佳
4、如何下载
打开idea plugins,切market place 搜索:Simple Object Copy

试用30天,支付宝、微信、PayPal都可以付款。 6元(人民币)每年,当然学生、教育机构、公益免费。
官方会邮件让你注册jb账号,给账号开通权限,你可以在官网找到自己的激活码,然后再idea 不登录用。也可以在idea 登录jb账号在线试用。
这可能是你的第一个付费插件。多谢支持,哈哈!
如果你没钱的话,也可邮件我。993198101@qq.com
下班早一半
通过插件的使用
1、可以节省一个个字段的设置的开发时间
2、避免了漏字段设置,ps:前端同学总是来问为啥字段总是null。
3、而且通过出入参的设计思想去开发,规范了代码,在有特殊请求转化的时候也比较方便。