• 参考
  1. (总结的不好)Java各种对象(PO,BO,VO,DTO,POJO,DAO,Entity,JavaBean,JavaBeans)的区分 -
  2. 企业级开发中,各种DAO、PO、DTO、BO的使用简介 -
  3. Bean自动映射工具对比及VO、DTO、PO、DO对象之间的转换

下面介绍的各种简写,虽然初看感觉故作姿态,看懂后感觉装神弄鬼,其实是为了方便沟通: 当项目变的庞大,开发人员不断流动的情况下,确定对象的操作范围和影响范围可以有效减少不必要的误会。

(💡感觉来来去去就是三层模型,只伴随各层范围的改变,有了MVC、…、DDD


文章目录

  • 示意图 ⭐️
  • PO (Persistent Object,持久化对象)【领域模型】
  • # 相同概念: DO (Data Object, 数据对象)
  • # 相同概念: Entity (实体)
  • DAO (Data Access Object,数据访问对象)
  • BO (Business Object,业务对象)
  • # 相同概念: DO (Domain Object) 【领域模型】
  • DTO (Data Transfer Object,数据传输对象)【领域模型】
  • VO (Value Object,值对象) ⭐️
  • # 相同概念: VO(View Object,表现层对象)【领域模型】
  • POJO (Plain Ordinary Java Object,简单普通的Java对象)
  • # 类似概念: JavaBean
  • # 类似概念: EJB(Enterprise JavaBean,企业级JavaBean)
  • 规范
  • # 阿里规范
  • 编程规约: 命名风格
  • 编程规约: OOP 规约
  • 工程结构: 应用分层
  • 转换工具
  • # ⚠️ Apache BeanUtils
  • # MapStruct ⭐️
  • 总结


示意图 ⭐️

下面基于MVC三层架构模型

我总结的图

java bean对象转成map_概念


客户端 Controller Service DAO Database queryString: ?id=aaa... 1 Json: {id:aaa...} 2 alt [request] VO、QueryVo、UpdateVo 3 PO(/DO/Entity) 4 sql: select * from tb_m 5 jdbc: resultSet 6 PO 7 PO 8 PO 9 par [BO] DTO 10 DTO 11 DTO 12 par [ResultVo] Json: {code:200, data: ...} 13 html: <html><head>...</head> <body>...</body></html> 14 alt [response] 客户端 Controller Service DAO Database


别人总结的图

java bean对象转成map_开发语言_02


文章目录

  • 示意图 ⭐️
  • PO (Persistent Object,持久化对象)【领域模型】
  • # 相同概念: DO (Data Object, 数据对象)
  • # 相同概念: Entity (实体)
  • DAO (Data Access Object,数据访问对象)
  • BO (Business Object,业务对象)
  • # 相同概念: DO (Domain Object) 【领域模型】
  • DTO (Data Transfer Object,数据传输对象)【领域模型】
  • VO (Value Object,值对象) ⭐️
  • # 相同概念: VO(View Object,表现层对象)【领域模型】
  • POJO (Plain Ordinary Java Object,简单普通的Java对象)
  • # 类似概念: JavaBean
  • # 类似概念: EJB(Enterprise JavaBean,企业级JavaBean)
  • 规范
  • # 阿里规范
  • 编程规约: 命名风格
  • 编程规约: OOP 规约
  • 工程结构: 应用分层
  • 转换工具
  • # ⚠️ Apache BeanUtils
  • # MapStruct ⭐️
  • 总结


PO (Persistent Object,持久化对象)【领域模型】

数据库表字段直接转成的对象: 当我们把数据从数据库提取出来后(或者存进去前),通常把数据封装成一个java对象,这时这个对象就是PO:

  • 数据库的列名和对象的属性名一一对应
  • 数据库的列类型和对象的数据类型一一对应
  • 一般,PO除了数据库提供的数据属性,除了get、set方法(和tostring、构造等一般性方法外),没有多余的方法

java bean对象转成map_java bean对象转成map_03

@Data
@Repository

# 相同概念: DO (Data Object, 数据对象)

DO(Data Object)是阿里巴巴在它们的开发手册中提出的概念。

这个概念其实等同于PO(Persisten Object)

💡 概念上: DO=PO

⚠️但是,这个简写DO(Data Object)与领域驱动设计DDD(Domain-Driven Design)中的DO(Domain Object)重名了。所以余私下认为把“数据库列表映射对象”称作PO更为妥当。

# 相同概念: Entity (实体)

Entity是ORM(Object Relation Mapping,对象/关系映射)中的概念。

ORM认为对象和关系型数据是业务实体的两种表现形式。业务实体在内存中表现为对象,在数据库中表现为关系型数据。

java bean对象转成map_开发语言_04

ORM的是概念性的东西,其具体实现的框架有:

  • JPA(Java Persistence API,Java的持久化API): Hibernate
  • myBatis
  • JOOQ
  • Exposed
  • Ktorm
  • Jimmer

可能后面没怎么听过,但相信过来的java开发肯定知道前面两个。

其中,JAP专门有一个注解 @Entity 就是用来标注Java类和数据库表的关系的。

其次,在mybatis中我们也习惯将po放在entity包下面。

因此,完全可以将po和orm中的entity划上等号

DAO (Data Access Object,数据访问对象)

要说DO(Data Object,数据对象)重点在于数据Data

DAO(Data Access Object,数据访问对象)与DO(Data Object,数据对象)不同:

  • DO重点在于数据(Data)
  • 而DAO重点在于“访问(Access)”二字

因此它实际上不存储数据,而是一个数据访问/写入的方法(接口),负责将数据库数据读出成PO或者将PO写入数据库。

💡 提示

在实际开发中,习惯把访问数据的方法归为一类,放在一起,叫做“数据访问对象层”,或者说“持久层(Persistence Layer)”

@Mapper

BO (Business Object,业务对象)

BO就是PO的组合(BO这个对象可以包含多个PO对象)、同时BO还能定义业务方法。

e.g.

我有一个业务要看个人简历(BO),个人简历包含:

  • 教育经历表的数据(PO)
  • 工作经历表的数据(PO)
  • 社会关系表的数据(PO)

于是

  1. (在Service Layer,业务层)让DAO将数据读出成PO,然后将读出的PO组成在一起形成“个人简历”对象(BO)
  2. 这个BO和仅有get、set方法的PO不同,在BO中还可以有业务相关的方法,比如:将简历数据发到第三方接口获取简历评分、将BO对象进一步封装成可以用于展示的VO对象的方法、…

@Service

# 相同概念: DO (Domain Object) 【领域模型】

领域对象,就是从现实世界中抽象出来的有形或无形的业务实体。

DTO (Data Transfer Object,数据传输对象)【领域模型】

用于表示一个数据传输对象,DTO 通常用于展示层(Controller)和服务层(Service)之间的数据传输对象。

DTO 的任务就是做VO和BO之间的桥梁。

e.g.

💡 订单列表
// bo
{
	user: {id: xxx, username: xxx, password: xxx, icon: xxx, ...},
	coupons: [{...}, {...}, {...}, ...],
	orderList: [
		{id: xxx, price: 255, pay: 200, merchant: {...}, buyer: {...}, coupons: [...], createTime: .....},
		{id: xxx, price: 255, pay: 200, merchant: {...}, buyer: {...}, coupons: [...], createTime: .....},
		{id: xxx, price: 255, pay: 200, merchant: {...}, buyer: {...}, coupons: [...], createTime: .....},
		{id: xxx, price: 255, pay: 200, merchant: {...}, buyer: {...}, coupons: [...], createTime: .....},
		...
	],
	...
}
// dto
{
	orderList: [
		{id: xxx, price: 255, pay: 200, merchant: {id: xxx, name: xxx}, coupons_size: 0, createTime: xxxx},
		{id: xxx, price: 255, pay: 200, merchant: {id: xxx, name: xxx}, coupons_size: 0, createTime: xxxx},
		{id: xxx, price: 255, pay: 200, merchant: {id: xxx, name: xxx}, coupons_size: 0, createTime: xxxx},
		{id: xxx, price: 255, pay: 200, merchant: {id: xxx, name: xxx}, coupons_size: 0, createTime: xxxx},
		...
	]
}
// vo
{
	status: 200,
	message: "操作成功!",
	data: [
		{order_id: xxx, order_price_total: 255, order_price_pay: 200, merchant: {id: xxx, name: xxx}, coupons_size: 0, createTime: xxxx},
		{order_id: xxx, order_price_total: 255, order_price_pay: 200, merchant: {id: xxx, name: xxx}, coupons_size: 0, createTime: xxxx},
		{order_id: xxx, order_price_total: 255, order_price_pay: 200, merchant: {id: xxx, name: xxx}, coupons_size: 0, createTime: xxxx},
		{order_id: xxx, order_price_total: 255, order_price_pay: 200, merchant: {id: xxx, name: xxx}, coupons_size: 0, createTime: xxxx},
		...
	],
	resp_time: "xxxx-xxx-xx"
}

💡除了做属性值的中转外,一个必须使用的场景是在使用JPA的场景下:

JPA查出来的PO有几种状态(【笔记】Spring Data JPA),这些状态都是需要事务会话(transaction session)支持的。可当PO出了service层后,事务会话一般就结束了,而PO是否会进一步需要事务会话是不确定的。因此,需要将PO转为DTO,从而摆脱数据对事务会话的依赖性和不再需要考虑数据状态的不确定性

VO (Value Object,值对象) ⭐️

表示一个与前端进行交互的视图对象,即用于某个接口返回前端页面的视图对象。

  1. 前到后: 用户发出请求,传递给后台controller接受的对象;
  2. 后到前:后端返回个前端的对象,可以 XXVO。

⚠️在开发过程中,有时为了偷懒或者方便,直接将DTO对象返回给前端,这样显得接口输出数据多余,和不安全性,dto作为数据传输对象一般是公用的,VO才是个性化,一旦前端说要说要增加或删字段返回给前端的dto对象 ,就会影响其他业务。因此,总整体性结构而言:vo是必须存在的,不能把dto直接返回给前端。高内聚,低耦合。

⚠️VO作为个性化视图对象,一般不建议复用。一般的数据传递是,前端传递VO给接口(Controller),接口将VO转为DTO传递给service,service将DTO分解为DO,调用领域服务进行调度,然后逆向转为VO或者其他的返回结果,传递给前台。

java bean对象转成map_开发语言_05

# 相同概念: VO(View Object,表现层对象)【领域模型】

同样是vo,翻译出来的含义也差不多。但个人感觉view有种调用模板引擎的感觉(前后端耦合),在前后端分离的大趋势下,View Object的翻译是不如Value Object的

POJO (Plain Ordinary Java Object,简单普通的Java对象)

顾名思义,POJO(Plain Ordinary Java Object)是一个简单的Java类,不用继承或实现任何接口,不用遵循任何框架的定义,不用担当任何业务的对象角色,就是一个不需要遵循任何规范的类。

同时,也可以说POJO遵循了什么规范,就是什么对象:

  • POJO遵循了JavaBean规范,就是JavaBean
  • 想将POJO持久化,就可以让POJO遵循PO规范: 属性名和数据库列名一一对应,且有get、set方法
# 类似概念: JavaBean

当人们说起JavaBean时,其实是想说一个类的规范:

  1. 属性用private修饰
  2. 属性有public的get、set方法
  3. 有一个无参构造方法

这种规范的制定就是为了某些框架(如:spring)通过反射(调用无参构造)构造这些JavaBean类的实例,且(调用get、set方法)为这些实例添加相关属性。

# 类似概念: EJB(Enterprise JavaBean,企业级JavaBean)

EJB这玩意其实是弄来给分布式用的,是一套用于java搭建分布式框架的规范,它是J2EE一部分。

那么它的核心模块如上图所示,分3个:

  1. 实体EJB
  2. 会话EJB
  3. 消息驱动Bean

todo

规范

# 阿里规范

看看阿里开发手册中关于POJO/DO/DTO/BO/VO的相关描述内容

编程规约: 命名风格
  1. 【强制】
  • 正例:ForceCode / UserDO / HtmlDTO / XmlService / TcpUdpDeal / TaPromotion
  • 反例:forcecode / UserDo / HTMLDto / XMLService / TCPUDPDeal / TAPromotion
  1. 【强制】
  2. 【参考】
  • 领域模型命名规约
  1. 数据对象:xxxDO,xxx 即为数据表名。
  2. 数据传输对象:xxxDTO,xxx 为业务领域相关的名称。
  3. 展示对象:xxxVO,xxx 一般为网页名称。
  4. POJO 是 DO/DTO/BO/VO 的统称,禁止命名成 xxxPOJO。
编程规约: OOP 规约
  1. 【强制】 定义 DO/DTO/VO 等 POJO 类时,不要设定任何属性默认值。
  • 反例: POJO 类的 createTime 默认值为 new Date(),但是这个属性在数据提取时并没有置入具体值,在更新其它字段时又附带更新了此字段,导致创建时间被修改成当前时间。
工程结构: 应用分层
  • 【参考】 分层领域模型规约:
  • DO(Data Object):此对象与数据库表结构一一对应,通过 DAO 层向上传输数据源对
  • DTO(Data Transfer Object):数据传输对象,Service 或 Manager 向外传输的对象
  • BO(Business Object):业务对象,可以由 Service 层输出的封装业务逻辑的对象。
  • Query:数据查询对象,各层接收上层的查询请求。注意超过 2 个参数的查询封装,禁止使用 Map 类来传输。
  • VO(View Object):显示层对象,通常是 Web 向模板渲染引擎层传输的对象。

转换工具

对于代码中 JavaBean之间的转换, 一直是困扰我很久的事情。在开发的时候我看到业务代码之间有很多的 JavaBean 之间的相互转化, 非常的影响观感,却又不得不存在。我后来想的一个办法就是通过反射,或者自己写很多的转换器。

  1. 第一种通过反射的方法确实比较方便,但是现在无论是 BeanUtils, BeanCopier 等在使用反射的时候都会影响到性能。虽然我们可以进行反射信息的缓存来提高性能。但是像这种的话,需要类型和名称都一样才会进行映射,有很多时候,由于不同的团队之间使用的名词不一样,还是需要很多的手动 set/get 等功能。
  2. 第二种的话就是会很浪费时间,而且在添加新的字段的时候也要进行方法的修改。不过,由于不需要进行反射,其性能是很高的。

框架或工具类

说明

commons包BeanUtils

spring beans下BeanUtils

Dozer

http://dozer.sourceforge.net/documentation/gettingstarted.html

Orika

https://orika-mapper.github.io/orika-docs/

MapStruct

https://mapstruct.org/

ModelMapper

http://modelmapper.org/

JMapper

https:///jmapper-framework/jmapper-core/wiki

# ⚠️ Apache BeanUtils

⚡这个不要用,性能差⚡
⚡这个不要用,性能差⚡
⚡这个不要用,性能差⚡

# MapStruct ⭐️


总结

概念是规范来给人用的,需要考虑到实际项目情况和团队使用情况做取舍,没必要意味无脑贯彻。

如果可以提出自己实践的概念、规范,那更是好。比如传统MVC较至于微服务、微服务较至于DDD。