一文总结POJO、DTO、DAO、PO、BO、VO、QO、ENTITY概念及详解

在java开发过程中,新手总是被DAO、PO、BO、VO等等概念弄得晕头转向。
下面我查找了很多资料,总结如下:

一、POJO(Plain Ordinary Java Object 简单Java对象)

实际就是普通JavaBeans,是为了避免和EJB(Enterprise Java Beans 企业级JavaBeans)混淆所创造的简称,也称为(Plain Old Java Object 又普通又老的对象)。

相比于EJB来说,的确是老的对象,因为ORM中间件的日趋流行,POJO又重新焕发了光彩。

POJO的内在含义是指:那些没有继承任何类、也没有实现任何接口,更没有被其它框架侵入的java对象

它仅包含自身的属性以及自身属性的getter和setter方法,这意味者POJO可以方便的从一个框架迁移到另一个框架中,或者框架升级也会对代码毫无影响,因此而得到复用。

//例如在该实体EJB中,实体包含业务逻辑,同时也包含自身的持久化逻辑
//当更换数据源,或改变中间件框架时,则需要修改大量代码
public class Customer {
	private Long id;
	private String name;
	private Set<Order> orders = new HashSet();
	//省略业务逻辑
	//数据库访问方法
	public void remove(){
		//通过不同方式访问数据库,例如JDBC,Mybaits,JPA
	}
	public Customer load(){...}
	public Customer create(){...}
}

//当改为POJO时,则可以运行在任一JAVA环境中
public class Customer {
	private Long id;
	private String name;
	private Set<Order> orders = new HashSet();
	//省略getter和setter
}

一般,当需要持久化对象时,人们喜欢将该对象放在名为xxxPOJO的目录中。当然,不建议这样命名。
POJO实际上是包括BO/VO/PO/DO等一系列对象的总称。有的团队规定禁止命名成xxxPOJO。


二、PO(persistence object 持久层对象)

对象字段持有的数据需要被持久化到数据表中,参与到持久化操作的对象就被称为持久化对象(persistence object,PO)。

通常,PO是在ORM(对象关系映射)中与数据表的一条记录相匹配,自身属性与数据表字段一一对应。可以将数据表中的一条记录作为一个对象处理,并可以转化为其它对象。

面对不同的数据源时,比如文档型数据库,对象型数据库等时,顾名思义PO是DAO层为进行持久化操作而准备的对象

  • 包含getter、setter方法。
  • 一般不包含业务逻辑与数据库的访问方法。因为数据库本身不包含业务逻辑。
  • PO平常不一定需要实现序列化,只是当采用分布式存储或者需要作为前端输出及远程调用使用时,应该实现序列化
//示例代码
public class User implements Serializable {
    //序列化版本,通过IDEA自动生成
    private static final long serialiVersionUID = 1Lprivate Long id;
    private String username;
    private String password;    
    //省略getter和setter方法
}

注意,持久化对象并不一定就是ORM机制(对象关系映射)的数据对象,相反,在领域驱动设计中,持久化对象往往指的就是领域模型对象。

在《阿里巴巴开发手册》中,PO也叫DO(Data Object)数据对象,与数据库表结构 一 一对应,通过DAO层向上传输数据源对象。


三、DAO(data access object 数据访问对象)

包含对数据的访问,负责持久层的操作 。通常需要结合PO来访问数据库,主要用来封装对数据的访问,并不转化成其它对象。
在基于“事务脚本”的业务设计时,它包含业务逻辑。否则,一般只包含持久化的封装。

public interface Dao{
    int insert(User user);
    User selectById(long id);
}


四、DTO(Data Transfer Object 数据传输对象)

数据传输对象,是在应用网络层需要传输的对象,是一个为了减少方法调用次数而在进程间传输数据的对象。

在《阿里巴巴开发手册中》规定是Service 或 Manager 向外传输的对象。

DTO模式诞生的背景是分布式通信。考虑到网络传输的损耗与不可靠性,设计分布式服务需遵循一个总体原则:

  • 尽可能设计粗粒度的服务,每个服务的方法应代表一个完整的功能,而不是功能的一个步骤。
  • 粗粒度服务可以减少服务调用的次数,从而减少不必要的网络通信,同时也能避免对分布式事务的支持。
  • 某些人称这个为“值对象”,当然还是有稍许区别。值对象追求对象不可变,DTO的对象是可修改,可改变的。

1.什么是DTO?

从输入来看,在进行请求时,应用在接口接收传入对象,然后又转换成实体进行持久化。在此过程中,传入的对象就是DTO。
它的命名方式可能是Param、Query 、Command、等。Param 为查询参数对象,适用于各层,一般用做接受前端参数对象。Param 和 Query 的出现是为了不使用 Map 做为接收参数的对象。

从输出来看,在进行返回响应时,若数据表有100个字段,那么PO中就有100个属性,而界面可能只需要其中10个属性,那么查询数据库后,对象就需要由PO转化成DTO。
DTO可能还需要组合多个表查询到的对象成为一个大对象,以便减少网络的调用,或者给前端传输一些不在数据库中查到的属性,所以需要添加属性

//示例代码,继承实体类,从查询到的PO中添加属性返回给前端
public class UserDTO extends User {
    //序列化版本
    private static final long serialiVersionUID = 2L//用户标识
    private String username;
    public String getUsername(){return username;}
    public void setUsername(String username){this.username= username;}
    //添加额外属性
    private HashMap<String, Object> extProperties;        
    public HashMap<String, Object> getExtProperties() {
        return extProperties;
    }
    public void setExtProperties(HashMap<String, Object> extProperties) {
        this.extProperties = extProperties;
    }
}

2.DTO里面有什么?
一般不建议创建DTO,因为里面就一些属性和getter、setter方法,业务价值小,作用仅仅是在一次调用中传输几部分信息。
其中,属性必须为原始数据类型,因为DTO需要被序列化以便能在连接中传播。
只包含自身数据的存储,而不包含业务逻辑
在不同的客户端之间,通常需要DTO组装器完成领域对象和DTO之间的转化

public interface DTOAssembler {
	public DTO createDTO(DO domainObject);
	public void updateDO(DTO  dataTransferObject);
	public DO createDO(DTO  dataTransferObject);
}

3.DTO怎么使用?
对于不同的客户端展现,可以选择一次封装所有可能的数据组成DTO,也可以为每种展现创建不同的DTO。各有利弊。

  • 单个大DTO,减少调用次数,只用创建一次,但是难以掌握传输的数据
  • 不同的小DTO,要创建很多DTO,传输数据很清晰

对于输入方和输出方,可以共用一个DTO,也可以各准备一个DTO,视情况而定。

有部分团队 RPC 的请求和响应参数都通过 DTO 来承载,通过 XXRequestDTO 和 XXResponseDTO 来表示。


五、Entity(实体)

实体,顾名思义,实体需要给予一个唯一标识,以区分其它实体,而值对象VO不需要
实体应该有一个生命周期,是有状态的,例如抽象订单有一个唯一识别号,订单有从下单创建到最后交货完成的生命周期,实体对象的状态可以变化。

1.与VO(值对象)的区别:

  • 只要两个实体对象的唯一标识相等,就判断两个对象相等,即使其他属性不同。可以修改实体状态
  • VO(值对象)没有标识所有属性相等,才判断两个对象相等。只能创建新的值对象,不能修改

2.与PO(持久化对象)的区别:

  • PO与数据表的一条记录对应,通常为了方便存储,会给PO赋予了一个主键ID
    从而,PO也像实体一样具有了标识,Martin Flowler称之为委派标识,区别于实体标识。
    例如身份证号作为身份证的实体标识,唯一区分其他身份证。但是存储在数据库中,依然可能分配自增主键ID(1,2,3,…)。若值对象需要持久化,也会被分配委派标识,方便查询,或与外键关联。
  • 实体的标识与业务有关,PO的委派标识仅方便存储,与技术有关。
//示例代码
public class Entity{
    private int id;
    int getId(){return id;}
}


六、VO(value object 值对象)

值对象,通常用于业务层之间的数据传递,仅仅包含自身的数据。
与实体的区别是,没有唯一标识,无生命周期,内部值是不变的
与PO的区别是,PO只在数据层,作为存储。VO在商业逻辑层和表示层,作为一个概念整体。

  • 值对象通常是小而简单的对象,如货币、日期或地址这样的对象,判断相等时不根据标识ID。比如,年月日相同,就判断这两个对象相等。
  • 值对象易于创建,参数传递时通常是传递值,而不是传递引用。
  • 值对象不应被持久化,这个对象被创建后只能被引用,当没有引用时交给垃圾回收自动处理。
//示例代码,比如Address这样无唯一标识的就是值对象
public class Address{
    private String country;
    private String province;
    private String city;
    public Address(String country, String province, String city) {
        this.country= country;
        this.province= province;
        this.city= city;
    }
    //省略equals方法
}
//调用值对象,若要修改值对象,值对象不用维护,直接创建一个全新的Address对象
//原对象直接被抛弃,而不是在原对象上进行修改
public Address changeAddress(String newCity, String newProvince, String newCity) {
    return new Address(newCity, newProvince, newCity);
}

理解了VO的意思,也就明白了,某些DTO也是一种值对象。历史上,在Martin Flowler称“值对象”的术语,在J2EE社团中称为“数据传输对象”,这在设计模式界引起了一场混乱。


七、VO( View Object 显示层对象)

Value Object和View Object的简写都是VO,可能容易弄混。View Object的含义是通常是Web向模板渲染引擎层传输的对象。《阿里巴巴开发手册》中建议把输出的显示层对象命名为VO。当然,也有人把这个对象命名为DTO作为传输。

//Controller层
public HttpResult list(@RequestBody XXParam param){
    HttpResult vo=HttpResultUtils.convert(XXDTO);
    return vo;
}

由于很多页面需要额外数据,比如错误码、提示信息、分页信息等,查询的DTO之后需要再封装成View Object 显示层对象再显示出来

{
	errcode: "00000",
	errmsg: "ok",
	data: {
		pageNum: 1, 
		pageSize: 10, 
		totalPage: 1, 
		total: 4, 
		list: [...]
	}
}


八、BO(business object 业务对象)

业务对象(business object,BO)是企业领域用来描述业务概念的语义对象。这是一个非常宽泛的定义。

业务对象,就是把业务逻辑封装为一个对象(注意是对象本身的业务逻辑,而不是协调其它对象的逻辑),这个业务对象可以包括一个或多个其它的对象。

一些业务建模方法使用了业务对象的概念,如SAP定义的公共事业模型,就将客户相关信息抽象为合作伙伴、合同账户、合同、连接对象等业务对象。它是站在一个高层次角度的表述,并形成了高度抽象的业务概念。

如果系统采用经典三层架构,可认为业务对象就是定义在业务逻辑层中 封装了 业务逻辑的对象。

一般,实现业务的通常方式之一,是包括数据而不包含行为的领域对象(所谓“贫血模型”)+Service类来实现的。其中,业务逻辑是包含在service层里,随着业务不断演进,service类里的逻辑越来越复杂,越来越重,不利于重用。

而在《面向领域驱动设计》中,**将对象本身的逻辑也封装在对象中(所谓“充血模型”),而service类仅仅起协调作用,比如对领域对象的调用及其它工具的调用,不包含业务本身的逻辑,是轻量级的薄薄一层,名为应用服务层。**当业务不断演进时,通常只需要关注业务对象(BO)即可,而应用层面较少变化。

因此,业务对象,也是领域对象(Domain Object)的另一说法。

//示例代码
public class User implements Serializable{
    private String name;
    private String password;
    //对象自身的业务逻辑
    public void changePassword(String newPassword){
        this.setPassword(newPassword);
    }
    //省略getter和setter方法
}

业务对象(BO)通常是实体,或者是聚合根,包含多个实体或值对象,内部实现业务逻辑。

此对象在实际使用中有不一样的理解,有的团队将 BO 当作 Service 返回给上层的 “专用 DTO” 使用;而有的团队则当作 Service 层内保存中间信息数据的 “DTO” 或者上下文对象来使用(建议采用这种理解)。


九、QO(query object 查询对象)

数据查询对象,各层接收上层的查询请求。注意,【强制】如果超过2个参数的查询封装,则禁止使用Map类传输。
查询对象用于 Controller 层方法接收客户端的请求参数

public class MyQO {
  private String param1;
  private String param2;
  ...
}
@PostMapping("/post")
public String post(@RequestBody MyQO qo) {
  ......
}

以创建简历对象为例,简历可分为教育经历、工作经历、项目经历。先填写查询对象的信息,转换为业务对象,分成不同的表进行存储。

MyBO bo = service.createBO(qo);//创建业务对象
bo.dosomething();
//将业务对象存储在不同的表中
MyPO po1 = service.createPO1(bo);
dao1.save(po1);
MyPO po2 = service.createPO2(bo);
dao2.save(po2);
MyPO po3 = service.createPO3(bo);
dao3.save(po3);


十、AO (Application Object 应用对象)

一般用在控制层和服务层之间。有些团队会将前端查询的属性和保存的属性几乎一致的对象封装为 AO,如读取用户属性传给前端,用户在前端编辑了用户属性后传回后端。这种用法将 AO 用做 Param 和 VO 或 Param 和 DTO 的组合。

//Controller层
public HttpResult update(@RequestBody XXAO ao1){
	XXAO ao2 = XXService.update(ao1);
    HttpResult vo = HttpResultUtils.convert(ao2);
    return vo;
}


十一、总结

POJO:简单Java对象,它没有任何限制和特定的约定,是一个普通的Java对象。
DTO:数据传输对象,它是一个数据传输结构,通常用于不同进程间的数据传输,在不同层之间传递数据的对象。
DAO:数据访问对象,是一个数据访问模式,在应用程序中它通常扮演着对数据库的访问。
PO:持久化对象,通常指ORM(对象关系映射)中映射的数据库表对应的实体类。
BO:业务对象,是应用程序中业务逻辑的实现。
VO:值对象,它是一个用于存储数据的对象,通常是与UI/界面模型相关的对象。
QO(Query):查询对象,它主要用于定义查询条件和规则,用于接收前端传递的查询条件参数。
ENTITY:实体对象,是一个与业务相关的对象,通常是与应用程序领域模型相关的对象。
Param:表示请求参数,用于接收前端传递的参数
Command:表示命令,用于接收前端传递的命令参数

各对象的命名习惯

  • PO通常放在名为bean、entity、model目录中。
  • DAO本身就是一层,通常是DAO、mapper、repository目录。
  • BO通常在service、manager、business,domain,model目录中。
  • DTO通常在command、representation、DTO目录中。

————————————————

 版权声明:本文为博主<晓风残月淡>原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
 原文链接:https://blog.csdn.net/qq_40610003/article/details/109007539
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值