文章目录
- 前言
- 一、解释VO/PO/DTO
- 二、实际使用
- PO的使用
- 接口新增时-VO的使用
- 接口修改时-VO的使用
- 查询时-VO的使用
- DTO的使用
- 三、总结
前言
经常有人不明白VO/PO/DTO是干嘛的,不知道在实际项目中如何使用,本文通过代码展开介绍,耐心看完你就能明白VO/PO/DTO到底是干嘛的。
提示:以下是本篇文章正文内容,下面案例可供参考
一、解释VO/PO/DTO
名称 | 详情 |
VO | 主要用来和前端交互,如接收前端参数,返回给查询前端结果 |
PO | 对应数据库的表结构,不可修改,直接从数据库查出来的对象 |
DTO | 作为service/manager层修改/查询数据库时的入参参数 |
二、实际使用
接下来我们以创建/修改/查询/同步订单为例子来实际使用VO/PO/DTO。
PO的使用
PO的作用:对应数据库的表结构,不可修改,直接从数据库查出来的对象
首先我们要创建一个订单。
创建两个类Order和OrderItem 这两个类就是PO,对应数据的表不可修改
@Data
public class Order{
// 订单id
private Long id;
// 订单号
private String uniqueOrderNo;
// 购买人手机
private String phone;
}
@Data
public class OrderItem {
// 订单明细id
private Long id;
// 订单id
private Long orderId;
// 商品id
private Long productId;
// 购买数量
private Integer quantity;
}
接下来我们模拟一下前端保存订单。
接口新增时-VO的使用
先创建实体类来接收前端的参数
@Data
public class OrderAddVo {
// 订单号
private String uniqueOrderNo;
// 购买人手机
private String phone;
// 订单明细
private List<OrderItemAddVo> items;
}
@Data
public class OrderItemAddVo {
// 商品id
private Long productId;
// 购买数量
private Integer quantity;
}
保存接口
@PostMapping(value = "/add")
public ResponseEnvelope<Long>(@RequestBody OrderAddVo addVo){
// 保存到数据库
Long orderId = save(addVo);
return new ResponseEnvelope<>(orderId);
}
可以看到controller里面的add方法传了一个OrderAddVo -这个实体类就是VO
,那么为什么是AddVo难道还有修改的editVo吗?修改的时候不能公用吗?
没错确实还有editVo。
接口修改时-VO的使用
下面我们来修改订单
@Data
public class OrderEditVo {
// 订单id
private Long id;
// 购买人手机
private String phone;
// 订单明细
private List<OrderItemEditVo> items;
}
@Data
public class OrderItemEditVo {
// 订单明细id
private Long id;
// 商品id
private Long productId;
// 购买数量
private Integer quantity;
}
修改接口
@PostMapping(value = "/edit")
public ResponseEnvelope<Long>(OrderEditVo editVo){
Long orderId = edit(editVo);
return new ResponseEnvelope<>(orderId);
}
为什么我们不用之前的OrderAddVo呢?
我们观察OrderEditVo 类去掉了uniqueOrderNo订单号(一般不予许修改订单号),增加了订单id。
OrderItemEditVo 增加了订单明细id。
EditVo和AddVO参数在传递时是有区别的,有一些需要传递有些不需要。
我们修改/新增的接口尽量对应一个唯一的VO,这样才能清楚知道前端具体会传递哪些参数。
那只有新增和修改能用到VO吗?
我们看一下查询接口,现在需要提供一个根据id查询订单和订单明细的接口。
查询时-VO的使用
查询时VO的作用:
- 将数据库多个表的
数据组装
成一个VO返回给前端 -
数据脱敏
,不直接返回数据库的表接口,需要的字段才在VO中指定。 - 不返回数据库所有的字段,只返回前端需要的字段。
查询接口订单明细接口
@Data
public class OrderDetailVo {
// 订单id
private String id;
// 订单号
private String uniqueOrderNo;
// 订单明细
private List<OrderDetailItemVo> items;
}
@Data
public class OrderDetailItemVo {
// 订单明细id
private Long id;
// 订单id
private Long orderId;
// 商品
private String productName;
// 商品id
private Long productId;
// 购买数量
private Integer quantity;
}
查询接口
@GetMapping(value ="/get")
public ResponseEnvelope<OrderDetailVo>(Long id){
List<OrderDetailItemVo> itemsVos = new ArrayList<>();
// 查询数据库订单
Order order = getOrder(id);
// 查询数据库订单明细
List<OrderItem> items = getOrderItem(id);
items.forEach(item -> {
OrderDetailItemV v = new OrderDetailItemV();
BeanUtils.copyProperties(item, v);
// 查询商品名称
v.setProductName(getProductName(item.getProductId));
itemsVos.add(v);
});
OrderDetailVo detailVo = new OrderDetailVo();
// 复制属性
BeanUtils.copyProperties(order, detailVo);
detailVo.setItems(itemsVos);
return new ResponseEnvelope<>(orderId);
}
可以看到在查询方法里面执行了-查询商品名称,因为数据库的OrderItem 并没有保存商品名称字段,实际上很多时候我们都只是在数据库里引用了另一个表的id,前端需要的时候的时候我们会再去别的表查出来set给VO对象。
返回的OrderDetailVo 并没有phone字段,因为我们不希望暴露客户的手机号。
DTO的使用
为什么要使用DTO呢?
DTO的作用:
对于不同数据格式但是行为一致的,提供统一的方法入口
假设我们有个需要要求同步第三方的订单过来,第三方数据格式如下 。
一个订单对应一个ThirdPartyOrder
@Data
public class ThirdPartyOrder {
private Order order;
private List<OrderItem> items;
private OrderPay orderPay;
}
private static class Order{
// 订单号
private String uniqueOrderNo;
// 购买人手机
private String phone;
}
private static class OrderItem{
// 商品id
private String productId;
// 数量
private Integer quantity;
}
private static class OrderPay{
// 支付类型
private String payType;
// 支付流水号
private String panNo;
}
第三方订单,原来的add接口的区别在于数据结构不一致,并且多出支付信息OrderPay orderPay。
这样的数据结构会导致我们没有办法使用save(OrderAddVo addVo)方法,同样的保存逻辑只是数据结构有一点点差异。
所以这个时候要用到DTO
,定义一个save(OrderAddDto addDto),这个方法就是通用的保存订单的方法。
- 前端调用save(OrderAddDto addDto)方法时候需要转换实体,OrderAddVo-》OrderAddDto
- 第三方订单调用save(OrderAddDto addDto)方法时候需要转换实体,ThirdPartyOrder -》OrderAddDto
三、总结
po对应数据库表
新增时- addVo
@PostMapping(value = "/add")
public ResponseEnvelope<Long>(@RequestBody OrderAddVo addVo)
修改时-editVo
@PostMapping(value = "/edit")
public ResponseEnvelope<Long>(OrderEditVo editVo)
查询时-vo
@GetMapping(value ="/get")
public ResponseEnvelope<OrderDetailVo>(Long id)
通用方法-dto
save(OrderAddDto addTdo)
提示:每个公司在使用时可能名称不一样,但是其目的都是是一样的,无非就是规范接口入参出差,抽出通用方法
- 数据表有个对应的类
- 接收前端入参对应一个类
- 响应前端对应一个类
- service层/manager层通用方法一个类
转换实体工具类使用spring自带的BeanUtils.copyProperties(source,target)
。注意:对象中有字段是其他实体类,或者List<对象> list 是不会对其属性复制的
;